benchmark silo added
authorahmad <ahmad@dw-10.eecs.uci.edu>
Thu, 7 Feb 2019 23:22:50 +0000 (15:22 -0800)
committerahmad <ahmad@dw-10.eecs.uci.edu>
Thu, 7 Feb 2019 23:22:50 +0000 (15:22 -0800)
291 files changed:
README.md
silo/AUTHORS [new file with mode: 0644]
silo/BUILD [new file with mode: 0644]
silo/LICENSE [new file with mode: 0644]
silo/Makefile [new file with mode: 0644]
silo/README.md [new file with mode: 0644]
silo/allocator.cc [new file with mode: 0644]
silo/allocator.h [new file with mode: 0644]
silo/amd64.h [new file with mode: 0644]
silo/base_txn_btree.h [new file with mode: 0644]
silo/benchmarks/abstract_db.h [new file with mode: 0644]
silo/benchmarks/abstract_ordered_index.h [new file with mode: 0644]
silo/benchmarks/bdb_wrapper.cc [new file with mode: 0644]
silo/benchmarks/bdb_wrapper.h [new file with mode: 0644]
silo/benchmarks/bench.cc [new file with mode: 0644]
silo/benchmarks/bench.h [new file with mode: 0644]
silo/benchmarks/bid.cc [new file with mode: 0644]
silo/benchmarks/dbtest.cc [new file with mode: 0644]
silo/benchmarks/encstress.cc [new file with mode: 0644]
silo/benchmarks/gc_runner.sh [new file with mode: 0755]
silo/benchmarks/kvdb_wrapper.h [new file with mode: 0644]
silo/benchmarks/kvdb_wrapper_impl.h [new file with mode: 0644]
silo/benchmarks/masstree/README [new file with mode: 0644]
silo/benchmarks/masstree/kvrandom.cc [new file with mode: 0644]
silo/benchmarks/masstree/kvrandom.hh [new file with mode: 0644]
silo/benchmarks/masstree/kvtest.cc [new file with mode: 0644]
silo/benchmarks/mysql_wrapper.cc [new file with mode: 0644]
silo/benchmarks/mysql_wrapper.h [new file with mode: 0644]
silo/benchmarks/ndb_wrapper.h [new file with mode: 0644]
silo/benchmarks/ndb_wrapper_impl.h [new file with mode: 0644]
silo/benchmarks/plotter.py [new file with mode: 0644]
silo/benchmarks/queue.cc [new file with mode: 0644]
silo/benchmarks/results/NOTES.txt [new file with mode: 0644]
silo/benchmarks/results/ben-3-8-13.py [new file with mode: 0644]
silo/benchmarks/results/ben-4-10-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-3-13-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-3-14-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-3-16-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-3-18-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-3-21-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-3-22-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-3-23-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-3-26-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-4-10-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-5-18-13-tpcc-chains.txt [new file with mode: 0644]
silo/benchmarks/results/istc11-5-18-13-ycsb-chains.txt [new file with mode: 0644]
silo/benchmarks/results/istc11-5-18-13.py [new file with mode: 0644]
silo/benchmarks/results/istc11-8-28-13_cameraready.py [new file with mode: 0644]
silo/benchmarks/results/istc12-8-30-13_cameraready.py [new file with mode: 0644]
silo/benchmarks/results/istc3-10-23-13.py [new file with mode: 0644]
silo/benchmarks/results/istc3-7-27-13.py [new file with mode: 0644]
silo/benchmarks/results/istc3-7-31-13.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13_compress.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13_fake_compress.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13_fake_writes.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13_fake_writes_stride.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13_fake_writes_stride1.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13_log_reduce_size.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13_log_reduce_size_1.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13_log_reduce_size_nofsync.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-1-13_newbench.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-12-13.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-16-13_multipart_skew.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-19-13_cameraready.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-21-13_cameraready-1.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-21-13_cameraready.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-22-13_cameraready.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-22-13_cameraready_2.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-23-13_cameraready.py [new file with mode: 0644]
silo/benchmarks/results/istc3-8-24-13_cameraready.py [new file with mode: 0644]
silo/benchmarks/results/istc3-9-6-13.py [new file with mode: 0644]
silo/benchmarks/results/istc3-9-8-13.py [new file with mode: 0644]
silo/benchmarks/results/make_graphs-2.py [new file with mode: 0755]
silo/benchmarks/results/make_graphs-3.py [new file with mode: 0755]
silo/benchmarks/results/make_graphs-4.py [new file with mode: 0644]
silo/benchmarks/results/make_graphs-5.py [new file with mode: 0644]
silo/benchmarks/results/make_graphs-6.py [new file with mode: 0644]
silo/benchmarks/results/make_graphs.py [new file with mode: 0755]
silo/benchmarks/results/tom-1-22-13.py [new file with mode: 0644]
silo/benchmarks/results/tom-2-13-13.py [new file with mode: 0644]
silo/benchmarks/results/tom-2-6-13.py [new file with mode: 0644]
silo/benchmarks/runner.py [new file with mode: 0644]
silo/benchmarks/stats_runner.py [new file with mode: 0644]
silo/benchmarks/tpcc.cc [new file with mode: 0644]
silo/benchmarks/tpcc.h [new file with mode: 0644]
silo/benchmarks/ycsb.cc [new file with mode: 0644]
silo/btree.cc [new file with mode: 0644]
silo/btree.h [new file with mode: 0644]
silo/btree_choice.h [new file with mode: 0644]
silo/btree_impl.h [new file with mode: 0644]
silo/circbuf.h [new file with mode: 0644]
silo/compile.sh [new file with mode: 0755]
silo/config/config-backoff.h [new file with mode: 0644]
silo/config/config-factor-fake-compression.h [new file with mode: 0644]
silo/config/config-factor-gc-nowriteinplace.h [new file with mode: 0644]
silo/config/config-factor-gc.h [new file with mode: 0644]
silo/config/config-perf.h [new file with mode: 0644]
silo/config/config-sandbox.h [new file with mode: 0644]
silo/core.cc [new file with mode: 0644]
silo/core.h [new file with mode: 0644]
silo/counter.cc [new file with mode: 0644]
silo/counter.h [new file with mode: 0644]
silo/fileutils.h [new file with mode: 0644]
silo/imstring.h [new file with mode: 0644]
silo/lockguard.h [new file with mode: 0644]
silo/log2.hh [new file with mode: 0644]
silo/macros.h [new file with mode: 0644]
silo/marked_ptr.h [new file with mode: 0644]
silo/masstree/AUTHORS [new file with mode: 0644]
silo/masstree/GNUmakefile [new file with mode: 0644]
silo/masstree/GNUmakefile.in [new file with mode: 0644]
silo/masstree/LICENSE [new file with mode: 0644]
silo/masstree/README.md [new file with mode: 0644]
silo/masstree/_masstree_config.d [new file with mode: 0644]
silo/masstree/autom4te.cache/output.0 [new file with mode: 0644]
silo/masstree/autom4te.cache/output.1 [new file with mode: 0644]
silo/masstree/autom4te.cache/requests [new file with mode: 0644]
silo/masstree/autom4te.cache/traces.0 [new file with mode: 0644]
silo/masstree/autom4te.cache/traces.1 [new file with mode: 0644]
silo/masstree/btree_leaflink.hh [new file with mode: 0644]
silo/masstree/checkpoint.cc [new file with mode: 0644]
silo/masstree/checkpoint.hh [new file with mode: 0644]
silo/masstree/circular_int.hh [new file with mode: 0644]
silo/masstree/clp.c [new file with mode: 0644]
silo/masstree/clp.h [new file with mode: 0644]
silo/masstree/compiler.cc [new file with mode: 0644]
silo/masstree/compiler.hh [new file with mode: 0644]
silo/masstree/config.h [new file with mode: 0644]
silo/masstree/config.h.in [new file with mode: 0644]
silo/masstree/config.log [new file with mode: 0644]
silo/masstree/config.status [new file with mode: 0755]
silo/masstree/configure [new file with mode: 0755]
silo/masstree/configure.ac [new file with mode: 0644]
silo/masstree/doc/.gitignore [new file with mode: 0644]
silo/masstree/doc/GNUmakefile [new file with mode: 0644]
silo/masstree/doc/elements.mp [new file with mode: 0644]
silo/masstree/doc/elemfig.sty [new file with mode: 0644]
silo/masstree/doc/examples.mp [new file with mode: 0644]
silo/masstree/doc/insert1.mp [new file with mode: 0644]
silo/masstree/doc/masstree.mp [new file with mode: 0644]
silo/masstree/doc/patches.mp [new file with mode: 0644]
silo/masstree/doc/remove1.mp [new file with mode: 0644]
silo/masstree/doc/remove2.mp [new file with mode: 0644]
silo/masstree/doc/spec.tex [new file with mode: 0644]
silo/masstree/file.cc [new file with mode: 0644]
silo/masstree/file.hh [new file with mode: 0644]
silo/masstree/hashcode.hh [new file with mode: 0644]
silo/masstree/json.cc [new file with mode: 0644]
silo/masstree/json.hh [new file with mode: 0644]
silo/masstree/jsontest.cc [new file with mode: 0644]
silo/masstree/kpermuter.hh [new file with mode: 0644]
silo/masstree/ksearch.hh [new file with mode: 0644]
silo/masstree/kvio.cc [new file with mode: 0644]
silo/masstree/kvio.hh [new file with mode: 0644]
silo/masstree/kvproto.hh [new file with mode: 0644]
silo/masstree/kvrandom.cc [new file with mode: 0644]
silo/masstree/kvrandom.hh [new file with mode: 0644]
silo/masstree/kvrow.hh [new file with mode: 0644]
silo/masstree/kvstats.hh [new file with mode: 0644]
silo/masstree/kvtest.hh [new file with mode: 0644]
silo/masstree/kvthread.cc [new file with mode: 0644]
silo/masstree/kvthread.hh [new file with mode: 0644]
silo/masstree/local_vector.hh [new file with mode: 0644]
silo/masstree/log.cc [new file with mode: 0644]
silo/masstree/log.hh [new file with mode: 0644]
silo/masstree/masstree.hh [new file with mode: 0644]
silo/masstree/masstree_get.hh [new file with mode: 0644]
silo/masstree/masstree_insert.hh [new file with mode: 0644]
silo/masstree/masstree_key.hh [new file with mode: 0644]
silo/masstree/masstree_print.hh [new file with mode: 0644]
silo/masstree/masstree_remove.hh [new file with mode: 0644]
silo/masstree/masstree_scan.hh [new file with mode: 0644]
silo/masstree/masstree_split.hh [new file with mode: 0644]
silo/masstree/masstree_struct.hh [new file with mode: 0644]
silo/masstree/masstree_tcursor.hh [new file with mode: 0644]
silo/masstree/misc.cc [new file with mode: 0644]
silo/masstree/misc.hh [new file with mode: 0644]
silo/masstree/msgpack.cc [new file with mode: 0644]
silo/masstree/msgpack.hh [new file with mode: 0644]
silo/masstree/msgpacktest.cc [new file with mode: 0644]
silo/masstree/mtclient.cc [new file with mode: 0644]
silo/masstree/mtclient.hh [new file with mode: 0644]
silo/masstree/mtcounters.hh [new file with mode: 0644]
silo/masstree/mtd.cc [new file with mode: 0644]
silo/masstree/mttest.cc [new file with mode: 0644]
silo/masstree/nodeversion.hh [new file with mode: 0644]
silo/masstree/perfstat.cc [new file with mode: 0644]
silo/masstree/perfstat.hh [new file with mode: 0644]
silo/masstree/query_masstree.cc [new file with mode: 0644]
silo/masstree/query_masstree.hh [new file with mode: 0644]
silo/masstree/str.cc [new file with mode: 0644]
silo/masstree/str.hh [new file with mode: 0644]
silo/masstree/straccum.cc [new file with mode: 0644]
silo/masstree/straccum.hh [new file with mode: 0644]
silo/masstree/string.cc [new file with mode: 0644]
silo/masstree/string.hh [new file with mode: 0644]
silo/masstree/string_base.hh [new file with mode: 0644]
silo/masstree/string_slice.cc [new file with mode: 0644]
silo/masstree/string_slice.hh [new file with mode: 0644]
silo/masstree/stringbag.hh [new file with mode: 0644]
silo/masstree/test_atomics.cc [new file with mode: 0644]
silo/masstree/test_string.cc [new file with mode: 0644]
silo/masstree/testrunner.cc [new file with mode: 0644]
silo/masstree/testrunner.hh [new file with mode: 0644]
silo/masstree/timestamp.hh [new file with mode: 0644]
silo/masstree/value_array.cc [new file with mode: 0644]
silo/masstree/value_array.hh [new file with mode: 0644]
silo/masstree/value_bag.hh [new file with mode: 0644]
silo/masstree/value_string.cc [new file with mode: 0644]
silo/masstree/value_string.hh [new file with mode: 0644]
silo/masstree/value_versioned_array.cc [new file with mode: 0644]
silo/masstree/value_versioned_array.hh [new file with mode: 0644]
silo/masstree_btree.h [new file with mode: 0644]
silo/memory.cc [new file with mode: 0644]
silo/ndb_type_traits.h [new file with mode: 0644]
silo/new-benchmarks/abstract_db.h [new file with mode: 0644]
silo/new-benchmarks/abstract_ordered_index.h [new file with mode: 0644]
silo/new-benchmarks/bench.cc [new file with mode: 0644]
silo/new-benchmarks/bench.h [new file with mode: 0644]
silo/new-benchmarks/dbtest.cc [new file with mode: 0644]
silo/new-benchmarks/kvdb_database.h [new file with mode: 0644]
silo/new-benchmarks/ndb_database.h [new file with mode: 0644]
silo/new-benchmarks/tpcc.cc [new file with mode: 0644]
silo/new-benchmarks/tpcc.h [new file with mode: 0644]
silo/ownership_checker.h [new file with mode: 0644]
silo/persist_test.cc [new file with mode: 0644]
silo/prefetch.h [new file with mode: 0644]
silo/pxqueue.h [new file with mode: 0644]
silo/rcu.cc [new file with mode: 0644]
silo/rcu.h [new file with mode: 0644]
silo/record/cursor.h [new file with mode: 0644]
silo/record/encoder.h [new file with mode: 0644]
silo/record/inline_str.h [new file with mode: 0644]
silo/record/serializer.h [new file with mode: 0644]
silo/run.sh [new file with mode: 0755]
silo/scopedperf.hh [new file with mode: 0644]
silo/scripts/tester.py [new file with mode: 0755]
silo/scripts/tester.sh [new file with mode: 0755]
silo/small_unordered_map.h [new file with mode: 0644]
silo/small_vector.h [new file with mode: 0644]
silo/spinbarrier.h [new file with mode: 0644]
silo/spinlock.h [new file with mode: 0644]
silo/static_unordered_map.h [new file with mode: 0644]
silo/static_vector.h [new file with mode: 0644]
silo/stats_client.cc [new file with mode: 0644]
silo/stats_common.h [new file with mode: 0644]
silo/stats_server.cc [new file with mode: 0644]
silo/stats_server.h [new file with mode: 0644]
silo/str_arena.h [new file with mode: 0644]
silo/test.cc [new file with mode: 0644]
silo/third-party/lz4/LZ4 Streaming Format.odt [new file with mode: 0644]
silo/third-party/lz4/Makefile [new file with mode: 0644]
silo/third-party/lz4/bench.c [new file with mode: 0644]
silo/third-party/lz4/bench.h [new file with mode: 0644]
silo/third-party/lz4/cmake/CMakeLists.txt [new file with mode: 0644]
silo/third-party/lz4/cmake/pack/CMakeLists.txt [new file with mode: 0644]
silo/third-party/lz4/cmake/pack/release_COPYING.txt [new file with mode: 0644]
silo/third-party/lz4/fullbench.c [new file with mode: 0644]
silo/third-party/lz4/fuzzer.c [new file with mode: 0644]
silo/third-party/lz4/liblz4.so [new file with mode: 0755]
silo/third-party/lz4/lz4.c [new file with mode: 0644]
silo/third-party/lz4/lz4.h [new file with mode: 0644]
silo/third-party/lz4/lz4.o [new file with mode: 0644]
silo/third-party/lz4/lz4_encoder.h [new file with mode: 0644]
silo/third-party/lz4/lz4_format_description.txt [new file with mode: 0644]
silo/third-party/lz4/lz4c.c [new file with mode: 0644]
silo/third-party/lz4/lz4hc.c [new file with mode: 0644]
silo/third-party/lz4/lz4hc.h [new file with mode: 0644]
silo/third-party/lz4/lz4hc_encoder.h [new file with mode: 0644]
silo/third-party/lz4/xxhash.c [new file with mode: 0644]
silo/third-party/lz4/xxhash.h [new file with mode: 0644]
silo/third-party/lz4/xxhash.o [new file with mode: 0644]
silo/thread.cc [new file with mode: 0644]
silo/thread.h [new file with mode: 0644]
silo/ticker.cc [new file with mode: 0644]
silo/ticker.h [new file with mode: 0644]
silo/tuple.cc [new file with mode: 0644]
silo/tuple.h [new file with mode: 0644]
silo/txn.cc [new file with mode: 0644]
silo/txn.h [new file with mode: 0644]
silo/txn_btree.cc [new file with mode: 0644]
silo/txn_btree.h [new file with mode: 0644]
silo/txn_impl.h [new file with mode: 0644]
silo/txn_proto2_impl.cc [new file with mode: 0644]
silo/txn_proto2_impl.h [new file with mode: 0644]
silo/typed_txn_btree.h [new file with mode: 0644]
silo/util.h [new file with mode: 0644]
silo/varint.cc [new file with mode: 0644]
silo/varint.h [new file with mode: 0644]
silo/varkey.h [new file with mode: 0644]

index f06a3768338e5497f6d70ed3eea00a14f44a4977..15958b3965cc3dc3a09cbc533cd6ac5ddf5e07e6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -12,3 +12,9 @@ Corresponding command for Ubuntu is included for convenience.
 
 4. g++ Compiler that supports C++11
 -- sudo apt-get install g++
+
+5. packages for Silo
+
+sudo apt-get install libdb++-dev
+sudo apt-get install libaio-dev
+sudo apt-get install libjemalloc-dev
diff --git a/silo/AUTHORS b/silo/AUTHORS
new file mode 100644 (file)
index 0000000..e04ebfa
--- /dev/null
@@ -0,0 +1,8 @@
+Stephen Tu
+stephentu@csail.mit.edu
+
+Wenting Zheng
+wzheng@mit.edu
+
+Eddie Kohler
+kohler@seas.harvard.edu
diff --git a/silo/BUILD b/silo/BUILD
new file mode 100644 (file)
index 0000000..719a9d0
--- /dev/null
@@ -0,0 +1,18 @@
+Baseline:
+
+$ sudo apt-get install libnuma-dev
+
+To use with jemalloc:
+
+$ sudo apt-get install libjemalloc-dev
+
+Note that jemalloc2 has a bug which shows up in tests- make sure
+to use jemalloc3
+
+To use with tcmalloc:
+
+$ sudo apt-get install libgoogle-perftools-dev
+
+For benchmarks:
+
+$ sudo apt-get install libdb5.3++-dev libmysqld-dev libaio-dev
diff --git a/silo/LICENSE b/silo/LICENSE
new file mode 100644 (file)
index 0000000..dc1d496
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (C) 2013 Stephen Tu and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/silo/Makefile b/silo/Makefile
new file mode 100644 (file)
index 0000000..682b4cb
--- /dev/null
@@ -0,0 +1,279 @@
+-include config.mk
+
+### Options ###
+
+DEBUG ?= 0
+CHECK_INVARIANTS ?= 0
+
+# 0 = libc malloc
+# 1 = jemalloc
+# 2 = tcmalloc
+# 3 = flow
+USE_MALLOC_MODE ?= 1
+
+MYSQL ?= 1
+MYSQL_SHARE_DIR ?= /x/stephentu/mysql-5.5.29/build/sql/share
+
+# Available modes
+#   * perf
+#   * backoff
+#   * factor-gc
+#   * factor-gc-nowriteinplace
+#   * factor-fake-compression
+#   * sandbox
+MODE ?= perf
+
+# run with 'MASSTREE=0' to turn off masstree
+MASSTREE ?= 1
+
+###############
+
+DEBUG_S=$(strip $(DEBUG))
+CHECK_INVARIANTS_S=$(strip $(CHECK_INVARIANTS))
+EVENT_COUNTERS_S=$(strip $(EVENT_COUNTERS))
+USE_MALLOC_MODE_S=$(strip $(USE_MALLOC_MODE))
+MODE_S=$(strip $(MODE))
+MASSTREE_S=$(strip $(MASSTREE))
+MASSTREE_CONFIG:=--enable-max-key-len=1024
+
+ifeq ($(DEBUG_S),1)
+       OSUFFIX_D=.debug
+       MASSTREE_CONFIG+=--enable-assertions
+else
+       MASSTREE_CONFIG+=--disable-assertions
+endif
+ifeq ($(CHECK_INVARIANTS_S),1)
+       OSUFFIX_S=.check
+       MASSTREE_CONFIG+=--enable-invariants --enable-preconditions
+else
+       MASSTREE_CONFIG+=--disable-invariants --disable-preconditions
+endif
+ifeq ($(EVENT_COUNTERS_S),1)
+       OSUFFIX_E=.ectrs
+endif
+OSUFFIX=$(OSUFFIX_D)$(OSUFFIX_S)$(OSUFFIX_E)
+
+ifeq ($(MODE_S),perf)
+       O := out-perf$(OSUFFIX)
+       CONFIG_H = config/config-perf.h
+else ifeq ($(MODE_S),backoff)
+       O := out-backoff$(OSUFFIX)
+       CONFIG_H = config/config-backoff.h
+else ifeq ($(MODE_S),factor-gc)
+       O := out-factor-gc$(OSUFFIX)
+       CONFIG_H = config/config-factor-gc.h
+else ifeq ($(MODE_S),factor-gc-nowriteinplace)
+       O := out-factor-gc-nowriteinplace$(OSUFFIX)
+       CONFIG_H = config/config-factor-gc-nowriteinplace.h
+else ifeq ($(MODE_S),factor-fake-compression)
+       O := out-factor-fake-compression$(OSUFFIX)
+       CONFIG_H = config/config-factor-fake-compression.h
+else ifeq ($(MODE_S),sandbox)
+       O := out-sandbox$(OSUFFIX)
+       CONFIG_H = config/config-sandbox.h
+else
+       $(error invalid mode)
+endif
+
+CXXFLAGS := -g -Wall -std=c++0x
+CXXFLAGS += -MD -Ithird-party/lz4 -DCONFIG_H=\"$(CONFIG_H)\"
+ifeq ($(DEBUG_S),1)
+        CXXFLAGS += -fno-omit-frame-pointer -DDEBUG
+else
+        CXXFLAGS += -Werror -O2 -funroll-loops -fno-omit-frame-pointer
+endif
+ifeq ($(CHECK_INVARIANTS_S),1)
+       CXXFLAGS += -DCHECK_INVARIANTS
+endif
+ifeq ($(EVENT_COUNTERS_S),1)
+       CXXFLAGS += -DENABLE_EVENT_COUNTERS
+endif
+ifeq ($(MASSTREE_S),1)
+       CXXFLAGS += -DNDB_MASSTREE -include masstree/config.h
+       OBJDEP += masstree/config.h
+       O := $(O).masstree
+else
+       O := $(O).silotree
+endif
+
+TOP     := $(shell echo $${PWD-`pwd`})
+LDFLAGS := -lpthread -lnuma -lrt
+
+LZ4LDFLAGS := -Lthird-party/lz4 -llz4 -Wl,-rpath,$(TOP)/third-party/lz4
+
+ifeq ($(USE_MALLOC_MODE_S),1)
+        CXXFLAGS+=-DUSE_JEMALLOC
+        LDFLAGS+=-ljemalloc
+       MASSTREE_CONFIG+=--with-malloc=jemalloc
+else ifeq ($(USE_MALLOC_MODE_S),2)
+        CXXFLAGS+=-DUSE_TCMALLOC
+        LDFLAGS+=-ltcmalloc
+       MASSTREE_CONFIG+=--with-malloc=tcmalloc
+else ifeq ($(USE_MALLOC_MODE_S),3)
+        CXXFLAGS+=-DUSE_FLOW
+        LDFLAGS+=-lflow
+       MASSTREE_CONFIG+=--with-malloc=flow
+else
+       MASSTREE_CONFIG+=--with-malloc=malloc
+endif
+
+ifneq ($(strip $(CUSTOM_LDPATH)), )
+        LDFLAGS+=$(CUSTOM_LDPATH)
+endif
+
+SRCFILES = allocator.cc \
+       btree.cc \
+       core.cc \
+       counter.cc \
+       memory.cc \
+       rcu.cc \
+       stats_server.cc \
+       thread.cc \
+       ticker.cc \
+       tuple.cc \
+       txn_btree.cc \
+       txn.cc \
+       txn_proto2_impl.cc \
+       varint.cc
+
+ifeq ($(MASSTREE_S),1)
+MASSTREE_SRCFILES = masstree/compiler.cc \
+       masstree/str.cc \
+       masstree/string.cc \
+       masstree/straccum.cc \
+       masstree/json.cc
+endif
+
+OBJFILES := $(patsubst %.cc, $(O)/%.o, $(SRCFILES))
+
+MASSTREE_OBJFILES := $(patsubst masstree/%.cc, $(O)/%.o, $(MASSTREE_SRCFILES))
+
+BENCH_CXXFLAGS := $(CXXFLAGS)
+BENCH_LDFLAGS := $(LDFLAGS) -ldb_cxx -lz -lrt -lcrypt -laio -ldl -lssl -lcrypto
+
+BENCH_SRCFILES = benchmarks/bdb_wrapper.cc \
+       benchmarks/bench.cc \
+       benchmarks/encstress.cc \
+       benchmarks/bid.cc \
+       benchmarks/masstree/kvrandom.cc \
+       benchmarks/queue.cc \
+       benchmarks/tpcc.cc \
+       benchmarks/ycsb.cc
+
+ifeq ($(MYSQL_S),1)
+BENCH_CXXFLAGS += -DMYSQL_SHARE_DIR=\"$(MYSQL_SHARE_DIR)\"
+BENCH_LDFLAGS := -L/usr/lib/mysql -lmysqld $(BENCH_LDFLAGS)
+BENCH_SRCFILES += benchmarks/mysql_wrapper.cc
+else
+BENCH_CXXFLAGS += -DNO_MYSQL
+endif
+
+BENCH_OBJFILES := $(patsubst %.cc, $(O)/%.o, $(BENCH_SRCFILES))
+
+NEWBENCH_SRCFILES = new-benchmarks/bench.cc \
+       new-benchmarks/tpcc.cc
+
+NEWBENCH_OBJFILES := $(patsubst %.cc, $(O)/%.o, $(NEWBENCH_SRCFILES))
+
+all: $(O)/test
+
+$(O)/benchmarks/%.o: benchmarks/%.cc $(O)/buildstamp $(O)/buildstamp.bench $(OBJDEP)
+       @mkdir -p $(@D)
+       $(CXX) $(BENCH_CXXFLAGS) -c $< -o $@
+
+$(O)/benchmarks/masstree/%.o: benchmarks/masstree/%.cc $(O)/buildstamp $(O)/buildstamp.bench $(OBJDEP)
+       @mkdir -p $(@D)
+       $(CXX) $(BENCH_CXXFLAGS) -c $< -o $@
+
+$(O)/new-benchmarks/%.o: new-benchmarks/%.cc $(O)/buildstamp $(O)/buildstamp.bench $(OBJDEP)
+       @mkdir -p $(@D)
+       $(CXX) $(CXXFLAGS) -c $< -o $@
+
+$(O)/%.o: %.cc $(O)/buildstamp $(OBJDEP)
+       @mkdir -p $(@D)
+       $(CXX) $(CXXFLAGS) -c $< -o $@
+
+$(MASSTREE_OBJFILES) : $(O)/%.o: masstree/%.cc masstree/config.h
+       @mkdir -p $(@D)
+       $(CXX) $(CXXFLAGS) -include masstree/config.h -c $< -o $@
+
+third-party/lz4/liblz4.so:
+       make -C third-party/lz4 library
+
+.PHONY: test
+test: $(O)/test
+
+$(O)/test: $(O)/test.o $(OBJFILES) $(MASSTREE_OBJFILES) third-party/lz4/liblz4.so
+       $(CXX) -o $(O)/test $^ $(LDFLAGS) $(LZ4LDFLAGS)
+
+.PHONY: persist_test
+persist_test: $(O)/persist_test
+
+$(O)/persist_test: $(O)/persist_test.o third-party/lz4/liblz4.so
+       $(CXX) -o $(O)/persist_test $(O)/persist_test.o $(LDFLAGS) $(LZ4LDFLAGS)
+
+.PHONY: stats_client
+stats_client: $(O)/stats_client
+
+$(O)/stats_client: $(O)/stats_client.o
+       $(CXX) -o $(O)/stats_client $(O)/stats_client.o $(LDFLAGS)
+
+masstree/config.h: $(O)/buildstamp.masstree masstree/configure masstree/config.h.in
+       rm -f $@
+       cd masstree; ./configure $(MASSTREE_CONFIG)
+       if test -f $@; then touch $@; fi
+
+masstree/configure masstree/config.h.in: masstree/configure.ac
+       cd masstree && autoreconf -i && touch configure config.h.in
+
+.PHONY: dbtest
+dbtest: $(O)/benchmarks/dbtest
+
+$(O)/benchmarks/dbtest: $(O)/benchmarks/dbtest.o $(OBJFILES) $(MASSTREE_OBJFILES) $(BENCH_OBJFILES) third-party/lz4/liblz4.so
+       $(CXX) -o $(O)/benchmarks/dbtest $^ $(BENCH_LDFLAGS) $(LZ4LDFLAGS)
+
+.PHONY: kvtest
+kvtest: $(O)/benchmarks/masstree/kvtest
+
+$(O)/benchmarks/masstree/kvtest: $(O)/benchmarks/masstree/kvtest.o $(OBJFILES) $(BENCH_OBJFILES)
+       $(CXX) -o $(O)/benchmarks/masstree/kvtest $^ $(BENCH_LDFLAGS)
+
+.PHONY: newdbtest
+newdbtest: $(O)/new-benchmarks/dbtest
+
+$(O)/new-benchmarks/dbtest: $(O)/new-benchmarks/dbtest.o $(OBJFILES) $(MASSTREE_OBJFILES) $(NEWBENCH_OBJFILES) third-party/lz4/liblz4.so
+       $(CXX) -o $(O)/new-benchmarks/dbtest $^ $(LDFLAGS) $(LZ4LDFLAGS)
+
+DEPFILES := $(wildcard $(O)/*.d $(O)/*/*.d $(O)/*/*/*.d masstree/_masstree_config.d)
+ifneq ($(DEPFILES),)
+-include $(DEPFILES)
+endif
+
+ifeq ($(wildcard masstree/GNUmakefile.in),)
+INSTALL_MASSTREE := $(shell git submodule init; git submodule update)
+endif
+
+ifeq ($(MASSTREE_S),1)
+UPDATE_MASSTREE := $(shell cd ./`git rev-parse --show-cdup` && cur=`git submodule status --cached masstree | head -c 41 | tail -c +2` && if test -z `cd masstree; git rev-list -n1 $$cur^..HEAD 2>/dev/null`; then (echo Updating masstree... 1>&2; cd masstree; git checkout -f master >/dev/null; git pull; cd ..; git submodule update masstree); fi)
+endif
+
+ifneq ($(strip $(DEBUG_S).$(CHECK_INVARIANTS_S).$(EVENT_COUNTERS_S)),$(strip $(DEP_MAIN_CONFIG)))
+DEP_MAIN_CONFIG := $(shell mkdir -p $(O); echo >$(O)/buildstamp; echo "DEP_MAIN_CONFIG:=$(DEBUG_S).$(CHECK_INVARIANTS_S).$(EVENT_COUNTERS_S)" >$(O)/_main_config.d)
+endif
+
+ifneq ($(strip $(MYSQL_S)),$(strip $(DEP_BENCH_CONFIG)))
+DEP_BENCH_CONFIG := $(shell mkdir -p $(O); echo >$(O)/buildstamp.bench; echo "DEP_BENCH_CONFIG:=$(MYSQL_S)" >$(O)/_bench_config.d)
+endif
+
+ifneq ($(strip $(MASSTREE_CONFIG)),$(strip $(DEP_MASSTREE_CONFIG)))
+DEP_MASSTREE_CONFIG := $(shell mkdir -p $(O); echo >$(O)/buildstamp.masstree; echo "DEP_MASSTREE_CONFIG:=$(MASSTREE_CONFIG)" >masstree/_masstree_config.d)
+endif
+
+$(O)/buildstamp $(O)/buildstamp.bench $(O)/buildstamp.masstree:
+       @mkdir -p $(@D)
+       @echo >$@
+
+.PHONY: clean
+clean:
+       rm -rf out-*
+       make -C third-party/lz4 clean
diff --git a/silo/README.md b/silo/README.md
new file mode 100644 (file)
index 0000000..04a68c9
--- /dev/null
@@ -0,0 +1,58 @@
+Silo
+=====
+
+This project contains the prototype of the database system described in 
+
+    Speedy Transactions in Multicore In-Memory Databases 
+    Stephen Tu, Wenting Zheng, Eddie Kohler, Barbara Liskov, Samuel Madden 
+    SOSP 2013. 
+    http://people.csail.mit.edu/stephentu/papers/silo.pdf
+
+This code is an ongoing work in progress.
+
+Build
+-----
+
+There are several options to build. `MODE` is an important variable
+governing the type of build. The default is `MODE=perf`, see the
+Makefile for more options. `DEBUG=1` triggers a debug build (off by
+default). `CHECK_INVARIANTS=1` enables invariant checking. There are
+two targets: the default target which builds the test suite, and
+`dbtest` which builds the benchmark suite. Examples:
+
+    MODE=perf DEBUG=1 CHECK_INVARIANTS=1 make -j
+    MODE=perf make -j dbtest
+
+Each different combination of `MODE`, `DEBUG`, and `CHECK_INVARIANTS` triggers
+a unique output directory; for example, the first command above builds to
+`out-perf.debug.check.masstree`.
+
+Silo now uses [Masstree](https://github.com/kohler/masstree-beta) by default as
+the default index tree. To use the old tree, set `MASSTREE=0`.
+
+Running
+-------
+
+To run the tests, simply invoke `<outdir>/test` with no arguments. To run the
+benchmark suite, invoke `<outdir>/benchmarks/dbtest`. For now, look in
+`benchmarks/dbtest.cc` for documentation on the command line arguments. An
+example invocation for TPC-C is:
+
+    <outdir>/benchmarks/dbtest \
+        --verbose \
+        --bench tpcc \
+        --num-threads 28 \
+        --scale-factor 28 \
+        --runtime 30 \
+        --numa-memory 112G 
+
+Benchmarks
+----------
+
+To reproduce the graphs from the paper:
+
+    $ cd benchmarks
+    $ python runner.py /unused-dir <results-file-prefix>
+
+If you set `DRYRUN=True` in `runner.py`, then you get to see all the
+commands that would be issued by the benchmark script.
diff --git a/silo/allocator.cc b/silo/allocator.cc
new file mode 100644 (file)
index 0000000..6c3067a
--- /dev/null
@@ -0,0 +1,376 @@
+#include <sys/mman.h>
+#include <unistd.h>
+#include <map>
+#include <iostream>
+#include <cstring>
+#include <numa.h>
+
+#include "allocator.h"
+#include "spinlock.h"
+#include "lockguard.h"
+#include "static_vector.h"
+#include "counter.h"
+
+using namespace util;
+
+static event_counter evt_allocator_total_region_usage(
+    "allocator_total_region_usage_bytes");
+
+// page+alloc routines taken from masstree
+
+#ifdef MEMCHECK_MAGIC
+const allocator::pgmetadata *
+allocator::PointerToPgMetadata(const void *p)
+{
+  static const size_t hugepgsize = GetHugepageSize();
+  if (unlikely(!ManagesPointer(p)))
+    return nullptr;
+  const size_t cpu = PointerToCpu(p);
+  const regionctx &pc = g_regions[cpu];
+  if (p >= pc.region_begin)
+    return nullptr;
+  // round pg down to page
+  p = (const void *) ((uintptr_t)p & ~(hugepgsize-1));
+  const pgmetadata *pmd = (const pgmetadata *) p;
+  ALWAYS_ASSERT((pmd->unit_ % AllocAlignment) == 0);
+  ALWAYS_ASSERT((MAX_ARENAS * AllocAlignment) >= pmd->unit_);
+  return pmd;
+}
+#endif
+
+size_t
+allocator::GetHugepageSizeImpl()
+{
+  FILE *f = fopen("/proc/meminfo", "r");
+  assert(f);
+  char *linep = NULL;
+  size_t n = 0;
+  static const char *key = "Hugepagesize:";
+  static const int keylen = strlen(key);
+  size_t size = 0;
+  while (getline(&linep, &n, f) > 0) {
+    if (strstr(linep, key) != linep)
+      continue;
+    size = atol(linep + keylen) * 1024;
+    break;
+  }
+  fclose(f);
+  assert(size);
+  return size;
+}
+
+size_t
+allocator::GetPageSizeImpl()
+{
+  return sysconf(_SC_PAGESIZE);
+}
+
+bool
+allocator::UseMAdvWillNeed()
+{
+  static const char *px = getenv("DISABLE_MADV_WILLNEED");
+  static const std::string s = px ? to_lower(px) : "";
+  static const bool use_madv = !(s == "1" || s == "true");
+  return use_madv;
+}
+
+void
+allocator::Initialize(size_t ncpus, size_t maxpercore)
+{
+  static spinlock s_lock;
+  static bool s_init = false;
+  if (likely(s_init))
+    return;
+  lock_guard<spinlock> l(s_lock);
+  if (s_init)
+    return;
+  ALWAYS_ASSERT(!g_memstart);
+  ALWAYS_ASSERT(!g_memend);
+  ALWAYS_ASSERT(!g_ncpus);
+  ALWAYS_ASSERT(!g_maxpercore);
+
+  static const size_t hugepgsize = GetHugepageSize();
+
+  // round maxpercore to the nearest hugepagesize
+  maxpercore = slow_round_up(maxpercore, hugepgsize);
+
+  g_ncpus = ncpus;
+  g_maxpercore = maxpercore;
+
+  // mmap() the entire region for now, but just as a marker
+  // (this does not actually cause physical pages to be allocated)
+  // note: we allocate an extra hugepgsize so we can guarantee alignment
+  // of g_memstart to a huge page boundary
+
+  void * const x = mmap(nullptr, g_ncpus * g_maxpercore + hugepgsize,
+      PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (x == MAP_FAILED) {
+    perror("mmap");
+    ALWAYS_ASSERT(false);
+  }
+
+  void * const endpx = (void *) ((uintptr_t)x + g_ncpus * g_maxpercore + hugepgsize);
+  std::cerr << "allocator::Initialize()" << std::endl
+            << "  hugepgsize: " << hugepgsize << std::endl
+            << "  use MADV_WILLNEED: " << UseMAdvWillNeed() << std::endl
+            << "  mmap() region [" << x << ", " << endpx << ")" << std::endl;
+
+  g_memstart = reinterpret_cast<void *>(util::iceil(uintptr_t(x), hugepgsize));
+  g_memend = reinterpret_cast<char *>(g_memstart) + (g_ncpus * g_maxpercore);
+
+  ALWAYS_ASSERT(!(reinterpret_cast<uintptr_t>(g_memstart) % hugepgsize));
+  ALWAYS_ASSERT(reinterpret_cast<uintptr_t>(g_memend) <=
+      (reinterpret_cast<uintptr_t>(x) + (g_ncpus * g_maxpercore + hugepgsize)));
+
+  for (size_t i = 0; i < g_ncpus; i++) {
+    g_regions[i].region_begin =
+      reinterpret_cast<char *>(g_memstart) + (i * g_maxpercore);
+    g_regions[i].region_end   =
+      reinterpret_cast<char *>(g_memstart) + ((i + 1) * g_maxpercore);
+    std::cerr << "cpu" << i << " owns [" << g_regions[i].region_begin
+              << ", " << g_regions[i].region_end << ")" << std::endl;
+    ALWAYS_ASSERT(g_regions[i].region_begin < g_regions[i].region_end);
+    ALWAYS_ASSERT(g_regions[i].region_begin >= x);
+    ALWAYS_ASSERT(g_regions[i].region_end <= endpx);
+  }
+
+  s_init = true;
+}
+
+void
+allocator::DumpStats()
+{
+  std::cerr << "[allocator] ncpus=" << g_ncpus << std::endl;
+  for (size_t i = 0; i < g_ncpus; i++) {
+    const bool f = g_regions[i].region_faulted;
+    const size_t remaining =
+      intptr_t(g_regions[i].region_end) -
+      intptr_t(g_regions[i].region_begin);
+    std::cerr << "[allocator] cpu=" << i << " fully_faulted?=" << f
+              << " remaining=" << remaining << " bytes" << std::endl;
+  }
+}
+
+static void *
+initialize_page(void *page, const size_t pagesize, const size_t unit)
+{
+  INVARIANT(((uintptr_t)page % pagesize) == 0);
+
+#ifdef MEMCHECK_MAGIC
+  ::allocator::pgmetadata *pmd = (::allocator::pgmetadata *) page;
+  pmd->unit_ = unit;
+  page = (void *) ((uintptr_t)page + sizeof(*pmd));
+#endif
+
+  void *first = (void *)util::iceil((uintptr_t)page, (uintptr_t)unit);
+  INVARIANT((uintptr_t)first + unit <= (uintptr_t)page + pagesize);
+  void **p = (void **)first;
+  void *next = (void *)((uintptr_t)p + unit);
+  while ((uintptr_t)next + unit <= (uintptr_t)page + pagesize) {
+    INVARIANT(((uintptr_t)p % unit) == 0);
+    *p = next;
+#ifdef MEMCHECK_MAGIC
+    NDB_MEMSET(
+        (char *) p + sizeof(void **),
+        MEMCHECK_MAGIC, unit - sizeof(void **));
+#endif
+    p = (void **)next;
+    next = (void *)((uintptr_t)next + unit);
+  }
+  INVARIANT(((uintptr_t)p % unit) == 0);
+  *p = NULL;
+#ifdef MEMCHECK_MAGIC
+  NDB_MEMSET(
+      (char *) p + sizeof(void **),
+      MEMCHECK_MAGIC, unit - sizeof(void **));
+#endif
+  return first;
+}
+
+void *
+allocator::AllocateArenas(size_t cpu, size_t arena)
+{
+  INVARIANT(cpu < g_ncpus);
+  INVARIANT(arena < MAX_ARENAS);
+  INVARIANT(g_memstart);
+  INVARIANT(g_maxpercore);
+  static const size_t hugepgsize = GetHugepageSize();
+
+  regionctx &pc = g_regions[cpu];
+  pc.lock.lock();
+  if (likely(pc.arenas[arena])) {
+    // claim
+    void *ret = pc.arenas[arena];
+    pc.arenas[arena] = nullptr;
+    pc.lock.unlock();
+    return ret;
+  }
+
+  void * const mypx = AllocateUnmanagedWithLock(pc, 1); // releases lock
+  return initialize_page(mypx, hugepgsize, (arena + 1) * AllocAlignment);
+}
+
+void *
+allocator::AllocateUnmanaged(size_t cpu, size_t nhugepgs)
+{
+  regionctx &pc = g_regions[cpu];
+  pc.lock.lock();
+  return AllocateUnmanagedWithLock(pc, nhugepgs); // releases lock
+}
+
+void *
+allocator::AllocateUnmanagedWithLock(regionctx &pc, size_t nhugepgs)
+{
+  static const size_t hugepgsize = GetHugepageSize();
+
+  void * const mypx = pc.region_begin;
+
+  // check alignment
+  if (reinterpret_cast<uintptr_t>(mypx) % hugepgsize)
+    ALWAYS_ASSERT(false);
+
+  void * const mynewpx =
+    reinterpret_cast<char *>(mypx) + nhugepgs * hugepgsize;
+
+  if (unlikely(mynewpx > pc.region_end)) {
+    std::cerr << "allocator::AllocateUnmanagedWithLock():" << std::endl
+              << "  region ending at " << pc.region_end << " OOM" << std::endl;
+    ALWAYS_ASSERT(false); // out of memory otherwise
+  }
+
+  const bool needs_mmap = !pc.region_faulted;
+  pc.region_begin = mynewpx;
+  pc.lock.unlock();
+
+  evt_allocator_total_region_usage.inc(nhugepgs * hugepgsize);
+
+  if (needs_mmap) {
+    void * const x = mmap(mypx, hugepgsize, PROT_READ | PROT_WRITE,
+        MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+    if (unlikely(x == MAP_FAILED)) {
+      perror("mmap");
+      ALWAYS_ASSERT(false);
+    }
+    INVARIANT(x == mypx);
+    const int advice =
+      UseMAdvWillNeed() ? MADV_HUGEPAGE | MADV_WILLNEED : MADV_HUGEPAGE;
+    if (madvise(x, hugepgsize, advice)) {
+      perror("madvise");
+      ALWAYS_ASSERT(false);
+    }
+  }
+
+  return mypx;
+}
+
+void
+allocator::ReleaseArenas(void **arenas)
+{
+  // cpu -> [(head, tail)]
+  // XXX: use a small_map here?
+  std::map<size_t, static_vector<std::pair<void *, void *>, MAX_ARENAS>> m;
+  for (size_t arena = 0; arena < MAX_ARENAS; arena++) {
+    void *p = arenas[arena];
+    while (p) {
+      void * const pnext = *reinterpret_cast<void **>(p);
+      const size_t cpu = PointerToCpu(p);
+      auto it = m.find(cpu);
+      if (it == m.end()) {
+        auto &v = m[cpu];
+        v.resize(MAX_ARENAS);
+        *reinterpret_cast<void **>(p) = nullptr;
+        v[arena].first = v[arena].second = p;
+      } else {
+        auto &v = it->second;
+        if (!v[arena].second) {
+          *reinterpret_cast<void **>(p) = nullptr;
+          v[arena].first = v[arena].second = p;
+        } else {
+          *reinterpret_cast<void **>(p) = v[arena].first;
+          v[arena].first = p;
+        }
+      }
+      p = pnext;
+    }
+  }
+  for (auto &p : m) {
+    INVARIANT(!p.second.empty());
+    regionctx &pc = g_regions[p.first];
+    lock_guard<spinlock> l(pc.lock);
+    for (size_t arena = 0; arena < MAX_ARENAS; arena++) {
+      INVARIANT(bool(p.second[arena].first) == bool(p.second[arena].second));
+      if (!p.second[arena].first)
+        continue;
+      *reinterpret_cast<void **>(p.second[arena].second) = pc.arenas[arena];
+      pc.arenas[arena] = p.second[arena].first;
+    }
+  }
+}
+
+static void
+numa_hint_memory_placement(void *px, size_t sz, unsigned node)
+{
+  struct bitmask *bm = numa_allocate_nodemask();
+  numa_bitmask_setbit(bm, node);
+  numa_interleave_memory(px, sz, bm);
+  numa_free_nodemask(bm);
+}
+
+void
+allocator::FaultRegion(size_t cpu)
+{
+  static const size_t hugepgsize = GetHugepageSize();
+  ALWAYS_ASSERT(cpu < g_ncpus);
+  regionctx &pc = g_regions[cpu];
+  if (pc.region_faulted)
+    return;
+  lock_guard<std::mutex> l1(pc.fault_lock);
+  lock_guard<spinlock> l(pc.lock); // exclude other users of the allocator
+  if (pc.region_faulted)
+    return;
+  // mmap the entire region + memset it for faulting
+  if (reinterpret_cast<uintptr_t>(pc.region_begin) % hugepgsize)
+    ALWAYS_ASSERT(false);
+  const size_t sz =
+    reinterpret_cast<uintptr_t>(pc.region_end) -
+    reinterpret_cast<uintptr_t>(pc.region_begin);
+  void * const x = mmap(pc.region_begin, sz, PROT_READ | PROT_WRITE,
+      MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+  if (unlikely(x == MAP_FAILED)) {
+    perror("mmap");
+    std::cerr << "  cpu" << cpu
+              << " [" << pc.region_begin << ", " << pc.region_end << ")"
+              << std::endl;
+    ALWAYS_ASSERT(false);
+  }
+  ALWAYS_ASSERT(x == pc.region_begin);
+  const int advice =
+    UseMAdvWillNeed() ? MADV_HUGEPAGE | MADV_WILLNEED : MADV_HUGEPAGE;
+  if (madvise(x, sz, advice)) {
+    perror("madvise");
+    ALWAYS_ASSERT(false);
+  }
+  numa_hint_memory_placement(
+      pc.region_begin,
+      (uintptr_t)pc.region_end - (uintptr_t)pc.region_begin,
+      numa_node_of_cpu(cpu));
+  const size_t nfaults =
+    ((uintptr_t)pc.region_end - (uintptr_t)pc.region_begin) / hugepgsize;
+  std::cerr << "cpu" << cpu << " starting faulting region ("
+            << intptr_t(pc.region_end) - intptr_t(pc.region_begin)
+            << " bytes / " << nfaults << " hugepgs)" << std::endl;
+  timer t;
+  for (char *px = (char *) pc.region_begin;
+       px < (char *) pc.region_end;
+       px += CACHELINE_SIZE)
+    *px = 0xDE;
+  std::cerr << "cpu" << cpu << " finished faulting region in "
+            << t.lap_ms() << " ms" << std::endl;
+  pc.region_faulted = true;
+}
+
+void *allocator::g_memstart = nullptr;
+void *allocator::g_memend = nullptr;
+size_t allocator::g_ncpus = 0;
+size_t allocator::g_maxpercore = 0;
+percore<allocator::regionctx> allocator::g_regions;
diff --git a/silo/allocator.h b/silo/allocator.h
new file mode 100644 (file)
index 0000000..266e686
--- /dev/null
@@ -0,0 +1,145 @@
+#ifndef _NDB_ALLOCATOR_H_
+#define _NDB_ALLOCATOR_H_
+
+#include <cstdint>
+#include <iterator>
+#include <mutex>
+
+#include "util.h"
+#include "core.h"
+#include "macros.h"
+#include "spinlock.h"
+
+class allocator {
+public:
+
+  // our allocator doesn't let allocations exceed maxpercore over a single core
+  //
+  // Initialize can be called many times- but only the first call has effect.
+  //
+  // w/o calling Initialize(), behavior for this class is undefined
+  static void Initialize(size_t ncpus, size_t maxpercore);
+
+  static void DumpStats();
+
+  // returns an arena linked-list
+  static void *
+  AllocateArenas(size_t cpu, size_t sz);
+
+  // allocates nhugepgs * hugepagesize contiguous bytes from CPU's region and
+  // returns the raw, unmanaged pointer.
+  //
+  // Note that memory returned from here cannot be released back to the
+  // allocator, so this should only be used for data structures which live
+  // throughput the duration of the system (ie log buffers)
+  static void *
+  AllocateUnmanaged(size_t cpu, size_t nhugepgs);
+
+  static void
+  ReleaseArenas(void **arenas);
+
+  static const size_t LgAllocAlignment = 4; // all allocations aligned to 2^4 = 16
+  static const size_t AllocAlignment = 1 << LgAllocAlignment;
+  static const size_t MAX_ARENAS = 32;
+
+  static inline std::pair<size_t, size_t>
+  ArenaSize(size_t sz)
+  {
+    const size_t allocsz = util::round_up<size_t, LgAllocAlignment>(sz);
+    const size_t arena = allocsz / AllocAlignment - 1;
+    return std::make_pair(allocsz, arena);
+  }
+
+  // slow, but only needs to be called on initialization
+  static void
+  FaultRegion(size_t cpu);
+
+  // returns true if managed by this allocator, false otherwise
+  static inline bool
+  ManagesPointer(const void *p)
+  {
+    return p >= g_memstart && p < g_memend;
+  }
+
+  // assumes p is managed by this allocator- returns the CPU from which this pointer
+  // was allocated
+  static inline size_t
+  PointerToCpu(const void *p)
+  {
+    ALWAYS_ASSERT(p >= g_memstart);
+    ALWAYS_ASSERT(p < g_memend);
+    const size_t ret =
+      (reinterpret_cast<const char *>(p) -
+       reinterpret_cast<const char *>(g_memstart)) / g_maxpercore;
+    ALWAYS_ASSERT(ret < g_ncpus);
+    return ret;
+  }
+
+#ifdef MEMCHECK_MAGIC
+  struct pgmetadata {
+    uint32_t unit_; // 0-indexed
+  } PACKED;
+
+  // returns nullptr if p is not managed, or has not been allocated yet.
+  // p does not have to be properly aligned
+  static const pgmetadata *
+  PointerToPgMetadata(const void *p);
+#endif
+
+  static size_t
+  GetPageSize()
+  {
+    static const size_t sz = GetPageSizeImpl();
+    return sz;
+  }
+
+  static size_t
+  GetHugepageSize()
+  {
+    static const size_t sz = GetHugepageSizeImpl();
+    return sz;
+  }
+
+private:
+  static size_t GetPageSizeImpl();
+  static size_t GetHugepageSizeImpl();
+  static bool UseMAdvWillNeed();
+
+  struct regionctx {
+    regionctx()
+      : region_begin(nullptr),
+        region_end(nullptr),
+        region_faulted(false)
+    {
+      NDB_MEMSET(arenas, 0, sizeof(arenas));
+    }
+    regionctx(const regionctx &) = delete;
+    regionctx(regionctx &&) = delete;
+    regionctx &operator=(const regionctx &) = delete;
+
+    // set by Initialize()
+    void *region_begin;
+    void *region_end;
+
+    bool region_faulted;
+
+    spinlock lock;
+    std::mutex fault_lock; // XXX: hacky
+    void *arenas[MAX_ARENAS];
+  };
+
+  // assumes caller has the regionctx lock held, and
+  // will release the lock.
+  static void *
+  AllocateUnmanagedWithLock(regionctx &pc, size_t nhugepgs);
+
+  // [g_memstart, g_memstart + ncpus * maxpercore) is the region of memory mmap()-ed
+  static void *g_memstart;
+  static void *g_memend; // g_memstart + ncpus * maxpercore
+  static size_t g_ncpus;
+  static size_t g_maxpercore;
+
+  static percore<regionctx> g_regions CACHE_ALIGNED;
+};
+
+#endif /* _NDB_ALLOCATOR_H_ */
diff --git a/silo/amd64.h b/silo/amd64.h
new file mode 100644 (file)
index 0000000..6a8c673
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef _AMD64_H_
+#define _AMD64_H_
+
+#include "macros.h"
+#include <stdint.h>
+
+inline ALWAYS_INLINE void
+nop_pause()
+{
+  __asm volatile("pause" : :);
+}
+
+inline ALWAYS_INLINE uint64_t
+rdtsc(void)
+{
+  uint32_t hi, lo;
+  __asm volatile("rdtsc" : "=a"(lo), "=d"(hi));
+  return ((uint64_t)lo)|(((uint64_t)hi)<<32);
+}
+
+#endif /* _AMD64_H_ */
diff --git a/silo/base_txn_btree.h b/silo/base_txn_btree.h
new file mode 100644 (file)
index 0000000..8a38dd8
--- /dev/null
@@ -0,0 +1,495 @@
+#ifndef _NDB_BASE_TXN_BTREE_H_
+#define _NDB_BASE_TXN_BTREE_H_
+
+#include "btree_choice.h"
+#include "txn.h"
+#include "lockguard.h"
+#include "util.h"
+#include "ndb_type_traits.h"
+
+#include <string>
+#include <map>
+#include <type_traits>
+#include <memory>
+
+// each Transaction implementation should specialize this for special
+// behavior- the default implementation is just nops
+template <template <typename> class Transaction>
+struct base_txn_btree_handler {
+  static inline void on_construct() {} // called when initializing
+  static const bool has_background_task = false;
+};
+
+template <template <typename> class Transaction, typename P>
+class base_txn_btree {
+public:
+
+  typedef transaction_base::tid_t tid_t;
+  typedef transaction_base::size_type size_type;
+  typedef transaction_base::string_type string_type;
+  typedef concurrent_btree::string_type keystring_type;
+
+  base_txn_btree(size_type value_size_hint = 128,
+            bool mostly_append = false,
+            const std::string &name = "<unknown>")
+    : value_size_hint(value_size_hint),
+      name(name),
+      been_destructed(false)
+  {
+    base_txn_btree_handler<Transaction>::on_construct();
+  }
+
+  ~base_txn_btree()
+  {
+    if (!been_destructed)
+      unsafe_purge(false);
+  }
+
+  inline size_t
+  size_estimate() const
+  {
+    return underlying_btree.size();
+  }
+
+  inline size_type
+  get_value_size_hint() const
+  {
+    return value_size_hint;
+  }
+
+  inline void
+  set_value_size_hint(size_type value_size_hint)
+  {
+    this->value_size_hint = value_size_hint;
+  }
+
+  inline void print() {
+    underlying_btree.print();
+  }
+
+  /**
+   * only call when you are sure there are no concurrent modifications on the
+   * tree. is neither threadsafe nor transactional
+   *
+   * Note that when you call unsafe_purge(), this txn_btree becomes
+   * completely invalidated and un-usable. Any further operations
+   * (other than calling the destructor) are undefined
+   */
+  std::map<std::string, uint64_t> unsafe_purge(bool dump_stats = false);
+
+private:
+
+  struct purge_tree_walker : public concurrent_btree::tree_walk_callback {
+    virtual void on_node_begin(const typename concurrent_btree::node_opaque_t *n);
+    virtual void on_node_success();
+    virtual void on_node_failure();
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+    purge_tree_walker()
+      : purge_stats_nodes(0),
+        purge_stats_nosuffix_nodes(0) {}
+    std::map<size_t, size_t> purge_stats_tuple_record_size_counts; // just the record
+    std::map<size_t, size_t> purge_stats_tuple_alloc_size_counts; // includes overhead
+    //std::map<size_t, size_t> purge_stats_tuple_chain_counts;
+    std::vector<uint16_t> purge_stats_nkeys_node;
+    size_t purge_stats_nodes;
+    size_t purge_stats_nosuffix_nodes;
+
+    std::map<std::string, uint64_t>
+    dump_stats()
+    {
+      std::map<std::string, uint64_t> ret;
+      size_t v = 0;
+      for (std::vector<uint16_t>::iterator it = purge_stats_nkeys_node.begin();
+          it != purge_stats_nkeys_node.end(); ++it)
+        v += *it;
+      const double avg_nkeys_node = double(v)/double(purge_stats_nkeys_node.size());
+      const double avg_fill_factor = avg_nkeys_node/double(concurrent_btree::NKeysPerNode);
+      std::cerr << "btree node stats" << std::endl;
+      std::cerr << "    avg_nkeys_node: " << avg_nkeys_node << std::endl;
+      std::cerr << "    avg_fill_factor: " << avg_fill_factor << std::endl;
+      std::cerr << "    num_nodes: " << purge_stats_nodes << std::endl;
+      std::cerr << "    num_nosuffix_nodes: " << purge_stats_nosuffix_nodes << std::endl;
+      std::cerr << "record size stats (nbytes => count)" << std::endl;
+      for (std::map<size_t, size_t>::iterator it = purge_stats_tuple_record_size_counts.begin();
+          it != purge_stats_tuple_record_size_counts.end(); ++it)
+        std::cerr << "    " << it->first << " => " << it->second << std::endl;
+      std::cerr << "alloc size stats  (nbytes => count)" << std::endl;
+      for (std::map<size_t, size_t>::iterator it = purge_stats_tuple_alloc_size_counts.begin();
+          it != purge_stats_tuple_alloc_size_counts.end(); ++it)
+        std::cerr << "    " << (it->first + sizeof(dbtuple)) << " => " << it->second << std::endl;
+      //std::cerr << "chain stats  (length => count)" << std::endl;
+      //for (std::map<size_t, size_t>::iterator it = purge_stats_tuple_chain_counts.begin();
+      //    it != purge_stats_tuple_chain_counts.end(); ++it) {
+      //  std::cerr << "    " << it->first << " => " << it->second << std::endl;
+      //  ret["chain_" + std::to_string(it->first)] += it->second;
+      //}
+      //std::cerr << "deleted recored stats" << std::endl;
+      //std::cerr << "    logically_removed (total): " << (purge_stats_tuple_logically_removed_no_mark + purge_stats_tuple_logically_removed_with_mark) << std::endl;
+      //std::cerr << "    logically_removed_no_mark: " << purge_stats_tuple_logically_removed_no_mark << std::endl;
+      //std::cerr << "    logically_removed_with_mark: " << purge_stats_tuple_logically_removed_with_mark << std::endl;
+      return ret;
+    }
+#endif
+
+  private:
+    std::vector< std::pair<typename concurrent_btree::value_type, bool> > spec_values;
+  };
+
+protected:
+
+  // readers are placed here so they can be shared amongst
+  // derived implementations
+
+  template <typename Traits, typename Callback,
+            typename KeyReader, typename ValueReader>
+  struct txn_search_range_callback : public concurrent_btree::low_level_search_range_callback {
+    constexpr txn_search_range_callback(
+          Transaction<Traits> *t,
+          Callback *caller_callback,
+          KeyReader *key_reader,
+          ValueReader *value_reader)
+      : t(t), caller_callback(caller_callback),
+        key_reader(key_reader), value_reader(value_reader) {}
+
+    virtual void on_resp_node(const typename concurrent_btree::node_opaque_t *n, uint64_t version);
+    virtual bool invoke(const typename concurrent_btree::string_type &k, typename concurrent_btree::value_type v,
+                        const typename concurrent_btree::node_opaque_t *n, uint64_t version);
+
+  private:
+    Transaction<Traits> *const t;
+    Callback *const caller_callback;
+    KeyReader *const key_reader;
+    ValueReader *const value_reader;
+  };
+
+  template <typename Traits, typename ValueReader>
+  inline bool
+  do_search(Transaction<Traits> &t,
+            const typename P::Key &k,
+            ValueReader &value_reader);
+
+  template <typename Traits, typename Callback,
+            typename KeyReader, typename ValueReader>
+  inline void
+  do_search_range_call(Transaction<Traits> &t,
+                       const typename P::Key &lower,
+                       const typename P::Key *upper,
+                       Callback &callback,
+                       KeyReader &key_reader,
+                       ValueReader &value_reader);
+
+  template <typename Traits, typename Callback,
+            typename KeyReader, typename ValueReader>
+  inline void
+  do_rsearch_range_call(Transaction<Traits> &t,
+                        const typename P::Key &upper,
+                        const typename P::Key *lower,
+                        Callback &callback,
+                        KeyReader &key_reader,
+                        ValueReader &value_reader);
+
+  // expect_new indicates if we expect the record to not exist in the tree-
+  // is just a hint that affects perf, not correctness. remove is put with nullptr
+  // as value.
+  //
+  // NOTE: both key and value are expected to be stable values already
+  template <typename Traits>
+  void do_tree_put(Transaction<Traits> &t,
+                   const std::string *k,
+                   const typename P::Value *v,
+                   dbtuple::tuple_writer_t writer,
+                   bool expect_new);
+
+  concurrent_btree underlying_btree;
+  size_type value_size_hint;
+  std::string name;
+  bool been_destructed;
+};
+
+namespace private_ {
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, txn_btree_search_probe0, txn_btree_search_probe0_cg)
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, txn_btree_search_probe1, txn_btree_search_probe1_cg)
+}
+
+template <template <typename> class Transaction, typename P>
+template <typename Traits, typename ValueReader>
+bool
+base_txn_btree<Transaction, P>::do_search(
+    Transaction<Traits> &t,
+    const typename P::Key &k,
+    ValueReader &value_reader)
+{
+  t.ensure_active();
+
+  typename P::KeyWriter key_writer(&k);
+  const std::string * const key_str =
+    key_writer.fully_materialize(true, t.string_allocator());
+
+  // search the underlying btree to map k=>(btree_node|tuple)
+  typename concurrent_btree::value_type underlying_v{};
+  concurrent_btree::versioned_node_t search_info;
+  const bool found = this->underlying_btree.search(varkey(*key_str), underlying_v, &search_info);
+  if (found) {
+    const dbtuple * const tuple = reinterpret_cast<const dbtuple *>(underlying_v);
+    return t.do_tuple_read(tuple, value_reader);
+  } else {
+    // not found, add to absent_set
+    t.do_node_read(search_info.first, search_info.second);
+    return false;
+  }
+}
+
+template <template <typename> class Transaction, typename P>
+std::map<std::string, uint64_t>
+base_txn_btree<Transaction, P>::unsafe_purge(bool dump_stats)
+{
+  ALWAYS_ASSERT(!been_destructed);
+  been_destructed = true;
+  purge_tree_walker w;
+  scoped_rcu_region guard;
+  underlying_btree.tree_walk(w);
+  underlying_btree.clear();
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+  if (!dump_stats)
+    return std::map<std::string, uint64_t>();
+  return w.dump_stats();
+#else
+  return std::map<std::string, uint64_t>();
+#endif
+}
+
+template <template <typename> class Transaction, typename P>
+void
+base_txn_btree<Transaction, P>::purge_tree_walker::on_node_begin(const typename concurrent_btree::node_opaque_t *n)
+{
+  INVARIANT(spec_values.empty());
+  spec_values = concurrent_btree::ExtractValues(n);
+}
+
+template <template <typename> class Transaction, typename P>
+void
+base_txn_btree<Transaction, P>::purge_tree_walker::on_node_success()
+{
+  for (size_t i = 0; i < spec_values.size(); i++) {
+    dbtuple *tuple = (dbtuple *) spec_values[i].first;
+    INVARIANT(tuple);
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+    // XXX(stephentu): should we also walk the chain?
+    purge_stats_tuple_record_size_counts[tuple->is_deleting() ? 0 : tuple->size]++;
+    purge_stats_tuple_alloc_size_counts[tuple->alloc_size]++;
+    //purge_stats_tuple_chain_counts[tuple->chain_length()]++;
+#endif
+    if (base_txn_btree_handler<Transaction>::has_background_task) {
+#ifdef CHECK_INVARIANTS
+      lock_guard<dbtuple> l(tuple, false);
+#endif
+      if (!tuple->is_deleting()) {
+        INVARIANT(tuple->is_latest());
+        tuple->clear_latest();
+        tuple->mark_deleting();
+        dbtuple::release(tuple);
+      } else {
+        // enqueued already to background gc by the writer of the delete
+      }
+    } else {
+      // XXX: this path is probably not right
+      dbtuple::release_no_rcu(tuple);
+    }
+  }
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+  purge_stats_nkeys_node.push_back(spec_values.size());
+  purge_stats_nodes++;
+  for (size_t i = 0; i < spec_values.size(); i++)
+    if (spec_values[i].second)
+      goto done;
+  purge_stats_nosuffix_nodes++;
+done:
+#endif
+  spec_values.clear();
+}
+
+template <template <typename> class Transaction, typename P>
+void
+base_txn_btree<Transaction, P>::purge_tree_walker::on_node_failure()
+{
+  spec_values.clear();
+}
+
+template <template <typename> class Transaction, typename P>
+template <typename Traits>
+void base_txn_btree<Transaction, P>::do_tree_put(
+    Transaction<Traits> &t,
+    const std::string *k,
+    const typename P::Value *v,
+    dbtuple::tuple_writer_t writer,
+    bool expect_new)
+{
+  INVARIANT(k);
+  INVARIANT(!expect_new || v); // makes little sense to remove() a key you expect
+                               // to not be present, so we assert this doesn't happen
+                               // for now [since this would indicate a suboptimality]
+  t.ensure_active();
+
+  if (unlikely(t.is_snapshot())) {
+    const transaction_base::abort_reason r = transaction_base::ABORT_REASON_USER;
+    t.abort_impl(r);
+    throw transaction_abort_exception(r);
+  }
+  dbtuple *px = nullptr;
+  bool insert = false;
+retry:
+  if (expect_new) {
+    auto ret = t.try_insert_new_tuple(this->underlying_btree, k, v, writer);
+    INVARIANT(!ret.second || ret.first);
+    if (unlikely(ret.second)) {
+      const transaction_base::abort_reason r = transaction_base::ABORT_REASON_WRITE_NODE_INTERFERENCE;
+      t.abort_impl(r);
+      throw transaction_abort_exception(r);
+    }
+    px = ret.first;
+    if (px)
+      insert = true;
+  }
+  if (!px) {
+    // do regular search
+    typename concurrent_btree::value_type bv = 0;
+    if (!this->underlying_btree.search(varkey(*k), bv)) {
+      // XXX(stephentu): if we are removing a key and we can't find it, then we
+      // should just treat this as a read [of an empty-value], instead of
+      // explicitly inserting an empty node...
+      expect_new = true;
+      goto retry;
+    }
+    px = reinterpret_cast<dbtuple *>(bv);
+  }
+  INVARIANT(px);
+  if (!insert) {
+    // add to write set normally, as non-insert
+    t.write_set.emplace_back(px, k, v, writer, &this->underlying_btree, false);
+  } else {
+    // should already exist in write set as insert
+    // (because of try_insert_new_tuple())
+
+    // too expensive to be a practical check
+    //INVARIANT(t.find_write_set(px) != t.write_set.end());
+    //INVARIANT(t.find_write_set(px)->is_insert());
+  }
+}
+
+template <template <typename> class Transaction, typename P>
+template <typename Traits, typename Callback,
+          typename KeyReader, typename ValueReader>
+void
+base_txn_btree<Transaction, P>
+  ::txn_search_range_callback<Traits, Callback, KeyReader, ValueReader>
+  ::on_resp_node(
+    const typename concurrent_btree::node_opaque_t *n, uint64_t version)
+{
+  VERBOSE(std::cerr << "on_resp_node(): <node=0x" << util::hexify(intptr_t(n))
+               << ", version=" << version << ">" << std::endl);
+  VERBOSE(std::cerr << "  " << concurrent_btree::NodeStringify(n) << std::endl);
+  t->do_node_read(n, version);
+}
+
+template <template <typename> class Transaction, typename P>
+template <typename Traits, typename Callback,
+          typename KeyReader, typename ValueReader>
+bool
+base_txn_btree<Transaction, P>
+  ::txn_search_range_callback<Traits, Callback, KeyReader, ValueReader>
+  ::invoke(
+    const typename concurrent_btree::string_type &k, typename concurrent_btree::value_type v,
+    const typename concurrent_btree::node_opaque_t *n, uint64_t version)
+{
+  t->ensure_active();
+  VERBOSE(std::cerr << "search range k: " << util::hexify(k) << " from <node=0x" << util::hexify(n)
+                    << ", version=" << version << ">" << std::endl
+                    << "  " << *((dbtuple *) v) << std::endl);
+  const dbtuple * const tuple = reinterpret_cast<const dbtuple *>(v);
+  if (t->do_tuple_read(tuple, *value_reader))
+    return caller_callback->invoke(
+        (*key_reader)(k), value_reader->results());
+  return true;
+}
+
+template <template <typename> class Transaction, typename P>
+template <typename Traits, typename Callback,
+          typename KeyReader, typename ValueReader>
+void
+base_txn_btree<Transaction, P>::do_search_range_call(
+    Transaction<Traits> &t,
+    const typename P::Key &lower,
+    const typename P::Key *upper,
+    Callback &callback,
+    KeyReader &key_reader,
+    ValueReader &value_reader)
+{
+  t.ensure_active();
+  if (upper)
+    VERBOSE(std::cerr << "txn_btree(0x" << util::hexify(intptr_t(this))
+                 << ")::search_range_call [" << util::hexify(lower)
+                 << ", " << util::hexify(*upper) << ")" << std::endl);
+  else
+    VERBOSE(std::cerr << "txn_btree(0x" << util::hexify(intptr_t(this))
+                 << ")::search_range_call [" << util::hexify(lower)
+                 << ", +inf)" << std::endl);
+
+  typename P::KeyWriter lower_key_writer(&lower);
+  const std::string * const lower_str =
+    lower_key_writer.fully_materialize(true, t.string_allocator());
+
+  typename P::KeyWriter upper_key_writer(upper);
+  const std::string * const upper_str =
+    upper_key_writer.fully_materialize(true, t.string_allocator());
+
+  if (unlikely(upper_str && *upper_str <= *lower_str))
+    return;
+
+  txn_search_range_callback<Traits, Callback, KeyReader, ValueReader> c(
+                       &t, &callback, &key_reader, &value_reader);
+
+  varkey uppervk;
+  if (upper_str)
+    uppervk = varkey(*upper_str);
+  this->underlying_btree.search_range_call(
+      varkey(*lower_str), upper_str ? &uppervk : nullptr,
+      c, t.string_allocator()());
+}
+
+template <template <typename> class Transaction, typename P>
+template <typename Traits, typename Callback,
+          typename KeyReader, typename ValueReader>
+void
+base_txn_btree<Transaction, P>::do_rsearch_range_call(
+    Transaction<Traits> &t,
+    const typename P::Key &upper,
+    const typename P::Key *lower,
+    Callback &callback,
+    KeyReader &key_reader,
+    ValueReader &value_reader)
+{
+  t.ensure_active();
+
+  typename P::KeyWriter lower_key_writer(lower);
+  const std::string * const lower_str =
+    lower_key_writer.fully_materialize(true, t.string_allocator());
+
+  typename P::KeyWriter upper_key_writer(&upper);
+  const std::string * const upper_str =
+    upper_key_writer.fully_materialize(true, t.string_allocator());
+
+  if (unlikely(lower_str && *upper_str <= *lower_str))
+    return;
+
+  txn_search_range_callback<Traits, Callback, KeyReader, ValueReader> c(
+                       &t, &callback, &key_reader, &value_reader);
+
+  varkey lowervk;
+  if (lower_str)
+    lowervk = varkey(*lower_str);
+  this->underlying_btree.rsearch_range_call(
+      varkey(*upper_str), lower_str ? &lowervk : nullptr,
+      c, t.string_allocator()());
+}
+
+#endif /* _NDB_BASE_TXN_BTREE_H_ */
diff --git a/silo/benchmarks/abstract_db.h b/silo/benchmarks/abstract_db.h
new file mode 100644 (file)
index 0000000..ace2a72
--- /dev/null
@@ -0,0 +1,137 @@
+#ifndef _ABSTRACT_DB_H_
+#define _ABSTRACT_DB_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+
+#include "abstract_ordered_index.h"
+#include "../str_arena.h"
+
+/**
+ * Abstract interface for a DB. This is to facilitate writing
+ * benchmarks for different systems, making each system present
+ * a unified interface
+ */
+class abstract_db {
+public:
+
+  /**
+   * both get() and put() can throw abstract_abort_exception. If thrown,
+   * abort_txn() must be called (calling commit_txn() will result in undefined
+   * behavior).  Also if thrown, subsequently calling get()/put() will also
+   * result in undefined behavior)
+   */
+  class abstract_abort_exception {};
+
+  // ctor should open db
+  abstract_db() {}
+
+  // dtor should close db
+  virtual ~abstract_db() {}
+
+  /**
+   * an approximate max batch size for updates in a transaction.
+   *
+   * A return value of -1 indicates no maximum
+   */
+  virtual ssize_t txn_max_batch_size() const { return -1; }
+
+  virtual bool index_has_stable_put_memory() const { return false; }
+
+  // XXX(stephentu): laziness
+  virtual size_t
+  sizeof_txn_object(uint64_t txn_flags) const { NDB_UNIMPLEMENTED("sizeof_txn_object"); };
+
+  /**
+   * XXX(stephentu): hack
+   */
+  virtual void do_txn_epoch_sync() const {}
+
+  /**
+   * XXX(stephentu): hack
+   */
+  virtual void do_txn_finish() const {}
+
+  /** loader should be used as a performance hint, not for correctness */
+  virtual void thread_init(bool loader) {}
+
+  virtual void thread_end() {}
+
+  // [ntxns_persisted, ntxns_committed, avg latency]
+  virtual std::tuple<uint64_t, uint64_t, double>
+    get_ntxn_persisted() const { return std::make_tuple(0, 0, 0.0); }
+
+  virtual void reset_ntxn_persisted() { }
+
+  enum TxnProfileHint {
+    HINT_DEFAULT,
+
+    // ycsb profiles
+    HINT_KV_GET_PUT, // KV workloads over a single key
+    HINT_KV_RMW, // get/put over a single key
+    HINT_KV_SCAN, // KV scan workloads (~100 keys)
+
+    // tpcc profiles
+    HINT_TPCC_NEW_ORDER,
+    HINT_TPCC_PAYMENT,
+    HINT_TPCC_DELIVERY,
+    HINT_TPCC_ORDER_STATUS,
+    HINT_TPCC_ORDER_STATUS_READ_ONLY,
+    HINT_TPCC_STOCK_LEVEL,
+    HINT_TPCC_STOCK_LEVEL_READ_ONLY,
+  };
+
+  /**
+   * Initializes a new txn object the space pointed to by buf
+   *
+   * Flags is only for the ndb protocol for now
+   *
+   * [buf, buf + sizeof_txn_object(txn_flags)) is a valid ptr
+   */
+  virtual void *new_txn(
+      uint64_t txn_flags,
+      str_arena &arena,
+      void *buf,
+      TxnProfileHint hint = HINT_DEFAULT) = 0;
+
+  typedef std::map<std::string, uint64_t> counter_map;
+  typedef std::map<std::string, counter_map> txn_counter_map;
+
+  /**
+   * Reports things like read/write set sizes
+   */
+  virtual counter_map
+  get_txn_counters(void *txn) const
+  {
+    return counter_map();
+  }
+
+  /**
+   * Returns true on successful commit.
+   *
+   * On failure, can either throw abstract_abort_exception, or
+   * return false- caller should be prepared to deal with both cases
+   */
+  virtual bool commit_txn(void *txn) = 0;
+
+  /**
+   * XXX
+   */
+  virtual void abort_txn(void *txn) = 0;
+
+  virtual void print_txn_debug(void *txn) const {}
+
+  virtual abstract_ordered_index *
+  open_index(const std::string &name,
+             size_t value_size_hint,
+             bool mostly_append = false) = 0;
+
+  virtual void
+  close_index(abstract_ordered_index *idx) = 0;
+};
+
+#endif /* _ABSTRACT_DB_H_ */
diff --git a/silo/benchmarks/abstract_ordered_index.h b/silo/benchmarks/abstract_ordered_index.h
new file mode 100644 (file)
index 0000000..30a5f3e
--- /dev/null
@@ -0,0 +1,150 @@
+#ifndef _ABSTRACT_ORDERED_INDEX_H_
+#define _ABSTRACT_ORDERED_INDEX_H_
+
+#include <stdint.h>
+#include <string>
+#include <utility>
+#include <map>
+
+#include "../macros.h"
+#include "../str_arena.h"
+
+/**
+ * The underlying index manages memory for keys/values, but
+ * may choose to expose the underlying memory to callers
+ * (see put() and inesrt()).
+ */
+class abstract_ordered_index {
+public:
+
+  virtual ~abstract_ordered_index() {}
+
+  /**
+   * Get a key of length keylen. The underlying DB does not manage
+   * the memory associated with key. Returns true if found, false otherwise
+   */
+  virtual bool get(
+      void *txn,
+      const std::string &key,
+      std::string &value,
+      size_t max_bytes_read = std::string::npos) = 0;
+
+  class scan_callback {
+  public:
+    virtual ~scan_callback() {}
+    // XXX(stephentu): key is passed as (const char *, size_t) pair
+    // because it really should be the string_type of the underlying
+    // tree, but since abstract_ordered_index is not templated we can't
+    // really do better than this for now
+    //
+    // we keep value as std::string b/c we have more control over how those
+    // strings are generated
+    virtual bool invoke(const char *keyp, size_t keylen,
+                        const std::string &value) = 0;
+  };
+
+  /**
+   * Search [start_key, *end_key) if end_key is not null, otherwise
+   * search [start_key, +infty)
+   */
+  virtual void scan(
+      void *txn,
+      const std::string &start_key,
+      const std::string *end_key,
+      scan_callback &callback,
+      str_arena *arena = nullptr) = 0;
+
+  /**
+   * Search (*end_key, start_key] if end_key is not null, otherwise
+   * search (-infty, start_key] (starting at start_key and traversing
+   * backwards)
+   */
+  virtual void rscan(
+      void *txn,
+      const std::string &start_key,
+      const std::string *end_key,
+      scan_callback &callback,
+      str_arena *arena = nullptr) = 0;
+
+  /**
+   * Put a key of length keylen, with mapping of length valuelen.
+   * The underlying DB does not manage the memory pointed to by key or value
+   * (a copy is made).
+   *
+   * If a record with key k exists, overwrites. Otherwise, inserts.
+   *
+   * If the return value is not NULL, then it points to the actual stable
+   * location in memory where the value is located. Thus, [ret, ret+valuelen)
+   * will be valid memory, bytewise equal to [value, value+valuelen), since the
+   * implementations have immutable values for the time being. The value
+   * returned is guaranteed to be valid memory until the key associated with
+   * value is overriden.
+   */
+  virtual const char *
+  put(void *txn,
+      const std::string &key,
+      const std::string &value) = 0;
+
+  virtual const char *
+  put(void *txn,
+      std::string &&key,
+      std::string &&value)
+  {
+    return put(txn, static_cast<const std::string &>(key),
+                    static_cast<const std::string &>(value));
+  }
+
+  /**
+   * Insert a key of length keylen.
+   *
+   * If a record with key k exists, behavior is unspecified- this function
+   * is only to be used when you can guarantee no such key exists (ie in loading phase)
+   *
+   * Default implementation calls put(). See put() for meaning of return value.
+   */
+  virtual const char *
+  insert(void *txn,
+         const std::string &key,
+         const std::string &value)
+  {
+    return put(txn, key, value);
+  }
+
+  virtual const char *
+  insert(void *txn,
+         std::string &&key,
+         std::string &&value)
+  {
+    return insert(txn, static_cast<const std::string &>(key),
+                       static_cast<const std::string &>(value));
+  }
+
+  /**
+   * Default implementation calls put() with NULL (zero-length) value
+   */
+  virtual void remove(
+      void *txn,
+      const std::string &key)
+  {
+    put(txn, key, "");
+  }
+
+  virtual void remove(
+      void *txn,
+      std::string &&key)
+  {
+    remove(txn, static_cast<const std::string &>(key));
+  }
+
+  /**
+   * Only an estimate, not transactional!
+   */
+  virtual size_t size() const = 0;
+
+  /**
+   * Not thread safe for now
+   */
+  virtual std::map<std::string, uint64_t> clear() = 0;
+};
+
+#endif /* _ABSTRACT_ORDERED_INDEX_H_ */
diff --git a/silo/benchmarks/bdb_wrapper.cc b/silo/benchmarks/bdb_wrapper.cc
new file mode 100644 (file)
index 0000000..2ae8fb3
--- /dev/null
@@ -0,0 +1,101 @@
+#include <limits>
+
+#include "bdb_wrapper.h"
+#include "../macros.h"
+
+using namespace std;
+
+bdb_wrapper::bdb_wrapper(const string &envdir, const string &dbfile)
+  : env(0)
+{
+  env = new DbEnv(0);
+  ALWAYS_ASSERT(env->log_set_config(DB_LOG_IN_MEMORY, 1) == 0);
+  ALWAYS_ASSERT(env->set_lg_max(numeric_limits<uint32_t>::max()) == 0);
+  ALWAYS_ASSERT(env->set_lg_regionmax(numeric_limits<uint32_t>::max()) == 0);
+  //ALWAYS_ASSERT(env->set_lg_bsize(numeric_limits<uint32_t>::max()) == 0);
+  ALWAYS_ASSERT(env->set_flags(DB_TXN_NOSYNC, 1) == 0);
+  ALWAYS_ASSERT(env->set_cachesize(4, 0, 1) == 0);
+  ALWAYS_ASSERT(env->open(envdir.c_str(), DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_PRIVATE | DB_THREAD | DB_CREATE, 0) == 0);
+}
+
+bdb_wrapper::~bdb_wrapper()
+{
+  delete env;
+}
+
+void *
+bdb_wrapper::new_txn(
+    uint64_t txn_flags,
+    str_arena &arena,
+    void *buf, TxnProfileHint hint)
+{
+  DbTxn *txn = NULL;
+  ALWAYS_ASSERT(env->txn_begin(NULL, &txn, 0) == 0);
+  ALWAYS_ASSERT(txn != NULL);
+  return (void *) txn;
+}
+
+bool
+bdb_wrapper::commit_txn(void *p)
+{
+  return ((DbTxn *) p)->commit(0) == 0;
+}
+
+void
+bdb_wrapper::abort_txn(void *p)
+{
+  ALWAYS_ASSERT(((DbTxn *) p)->abort() == 0);
+}
+
+abstract_ordered_index *
+bdb_wrapper::open_index(const string &name, size_t value_size_hint, bool mostly_append)
+{
+  Db *db = new Db(env, 0);
+  ALWAYS_ASSERT(db->set_flags(DB_TXN_NOT_DURABLE) == 0);
+  DbTxn *txn = NULL;
+  ALWAYS_ASSERT(env->txn_begin(NULL, &txn, 0) == 0);
+  ALWAYS_ASSERT(db->open(txn, name.c_str(), NULL, DB_BTREE, DB_CREATE, 0) == 0);
+  ALWAYS_ASSERT(txn->commit(0) == 0);
+  return new bdb_ordered_index(db);
+}
+
+void
+bdb_wrapper::close_index(abstract_ordered_index *idx)
+{
+  bdb_ordered_index *bidx = static_cast<bdb_ordered_index *>(idx);
+  delete bidx;
+}
+
+bdb_ordered_index::~bdb_ordered_index()
+{
+  delete db;
+}
+
+bool
+bdb_ordered_index::get(
+    void *txn,
+    const string &key,
+    string &value,
+    size_t max_bytes_read)
+{
+  Dbt kdbt((void *) key.data(), key.size());
+  Dbt vdbt;
+  //vdbt.set_flags(DB_DBT_MALLOC);
+  int retno = db->get((DbTxn *) txn, &kdbt, &vdbt, 0);
+  ALWAYS_ASSERT(retno == 0 || retno == DB_NOTFOUND);
+  // XXX(stephentu): do a better job implementing this
+  value.assign((char *) vdbt.get_data(), min(static_cast<size_t>(vdbt.get_size()), max_bytes_read));
+  return retno == 0;
+}
+
+const char *
+bdb_ordered_index::put(
+    void *txn,
+    const string &key,
+    const string &value)
+{
+  Dbt kdbt((void *) key.data(), key.size());
+  Dbt vdbt((void *) value.data(), value.size());
+  ALWAYS_ASSERT(db->put((DbTxn *) txn, &kdbt, &vdbt, 0) == 0);
+  return 0;
+}
diff --git a/silo/benchmarks/bdb_wrapper.h b/silo/benchmarks/bdb_wrapper.h
new file mode 100644 (file)
index 0000000..f80e9a6
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef _BDB_WRAPPER_H_
+#define _BDB_WRAPPER_H_
+
+#include <string>
+#include <db_cxx.h>
+
+#include "abstract_db.h"
+#include "../macros.h"
+
+class bdb_wrapper : public abstract_db {
+public:
+  bdb_wrapper(const std::string &envdir,
+              const std::string &dbfile);
+  ~bdb_wrapper();
+
+  /**
+   * BDB has small txn sizes
+   */
+  virtual ssize_t txn_max_batch_size() const { return 1000; }
+
+  virtual void *new_txn(
+      uint64_t txn_flags,
+      str_arena &arena,
+      void *buf,
+      TxnProfileHint hint);
+  virtual bool commit_txn(void *txn);
+  virtual void abort_txn(void *txn);
+
+  virtual abstract_ordered_index *
+  open_index(const std::string &name,
+             size_t value_size_hint,
+             bool mostly_append);
+
+  virtual void
+  close_index(abstract_ordered_index *idx);
+
+private:
+  DbEnv *env;
+};
+
+class bdb_ordered_index : public abstract_ordered_index {
+public:
+
+  // takes ownership of db
+  bdb_ordered_index(Db *db) : db(db) {}
+  ~bdb_ordered_index();
+
+  virtual bool get(
+      void *txn,
+      const std::string &key,
+      std::string &value,
+      size_t max_bytes_read);
+
+  virtual const char * put(
+      void *txn,
+      const std::string &key,
+      const std::string &value);
+
+  virtual void scan(
+      void *txn,
+      const std::string &key,
+      const std::string *value,
+      scan_callback &callback,
+      str_arena *arena)
+  {
+    NDB_UNIMPLEMENTED("scan");
+  }
+
+  virtual void rscan(
+      void *txn,
+      const std::string &start_key,
+      const std::string *end_key,
+      scan_callback &callback,
+      str_arena *arena)
+  {
+    NDB_UNIMPLEMENTED("rscan");
+  }
+
+  virtual size_t
+  size() const
+  {
+    NDB_UNIMPLEMENTED("size");
+  }
+
+  virtual std::map<std::string, uint64_t>
+  clear()
+  {
+    NDB_UNIMPLEMENTED("clear");
+  }
+
+private:
+  Db *db;
+};
+
+#endif /* _BDB_WRAPPER_H_ */
diff --git a/silo/benchmarks/bench.cc b/silo/benchmarks/bench.cc
new file mode 100644 (file)
index 0000000..a1c12bb
--- /dev/null
@@ -0,0 +1,428 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <utility>
+#include <string>
+
+#include <stdlib.h>
+#include <sched.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#include "bench.h"
+
+#include "../counter.h"
+#include "../scopedperf.hh"
+#include "../allocator.h"
+
+#ifdef USE_JEMALLOC
+//cannot include this header b/c conflicts with malloc.h
+//#include <jemalloc/jemalloc.h>
+extern "C" void malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque, const char *opts);
+extern "C" int mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
+#endif
+#ifdef USE_TCMALLOC
+#include <google/heap-profiler.h>
+#endif
+
+using namespace std;
+using namespace util;
+
+size_t nthreads = 1;
+volatile bool running = true;
+int verbose = 0;
+uint64_t txn_flags = 0;
+double scale_factor = 1.0;
+uint64_t runtime = 30;
+uint64_t ops_per_worker = 0;
+int run_mode = RUNMODE_TIME;
+int enable_parallel_loading = false;
+int pin_cpus = 0;
+int slow_exit = 0;
+int retry_aborted_transaction = 0;
+int no_reset_counters = 0;
+int backoff_aborted_transaction = 0;
+
+template <typename T>
+static void
+delete_pointers(const vector<T *> &pts)
+{
+  for (size_t i = 0; i < pts.size(); i++)
+    delete pts[i];
+}
+
+template <typename T>
+static vector<T>
+elemwise_sum(const vector<T> &a, const vector<T> &b)
+{
+  INVARIANT(a.size() == b.size());
+  vector<T> ret(a.size());
+  for (size_t i = 0; i < a.size(); i++)
+    ret[i] = a[i] + b[i];
+  return ret;
+}
+
+template <typename K, typename V>
+static void
+map_agg(map<K, V> &agg, const map<K, V> &m)
+{
+  for (typename map<K, V>::const_iterator it = m.begin();
+       it != m.end(); ++it)
+    agg[it->first] += it->second;
+}
+
+// returns <free_bytes, total_bytes>
+static pair<uint64_t, uint64_t>
+get_system_memory_info()
+{
+  struct sysinfo inf;
+  sysinfo(&inf);
+  return make_pair(inf.mem_unit * inf.freeram, inf.mem_unit * inf.totalram);
+}
+
+static bool
+clear_file(const char *name)
+{
+  ofstream ofs(name);
+  ofs.close();
+  return true;
+}
+
+static void
+write_cb(void *p, const char *s) UNUSED;
+static void
+write_cb(void *p, const char *s)
+{
+  const char *f = "jemalloc.stats";
+  static bool s_clear_file UNUSED = clear_file(f);
+  ofstream ofs(f, ofstream::app);
+  ofs << s;
+  ofs.flush();
+  ofs.close();
+}
+
+static event_avg_counter evt_avg_abort_spins("avg_abort_spins");
+
+void
+bench_worker::run()
+{
+  // XXX(stephentu): so many nasty hacks here. should actually
+  // fix some of this stuff one day
+  if (set_core_id)
+    coreid::set_core_id(worker_id); // cringe
+  {
+    scoped_rcu_region r; // register this thread in rcu region
+  }
+  on_run_setup();
+  scoped_db_thread_ctx ctx(db, false);
+  const workload_desc_vec workload = get_workload();
+  txn_counts.resize(workload.size());
+  barrier_a->count_down();
+  barrier_b->wait_for();
+  while (running && (run_mode != RUNMODE_OPS || ntxn_commits < ops_per_worker)) {
+    double d = r.next_uniform();
+    for (size_t i = 0; i < workload.size(); i++) {
+      if ((i + 1) == workload.size() || d < workload[i].frequency) {
+      retry:
+        timer t;
+        const unsigned long old_seed = r.get_seed();
+        const auto ret = workload[i].fn(this);
+        if (likely(ret.first)) {
+          ++ntxn_commits;
+          latency_numer_us += t.lap();
+          backoff_shifts >>= 1;
+        } else {
+          ++ntxn_aborts;
+          if (retry_aborted_transaction && running) {
+            if (backoff_aborted_transaction) {
+              if (backoff_shifts < 63)
+                backoff_shifts++;
+              uint64_t spins = 1UL << backoff_shifts;
+              spins *= 100; // XXX: tuned pretty arbitrarily
+              evt_avg_abort_spins.offer(spins);
+              while (spins) {
+                nop_pause();
+                spins--;
+              }
+            }
+            r.set_seed(old_seed);
+            goto retry;
+          }
+        }
+        size_delta += ret.second; // should be zero on abort
+        txn_counts[i]++; // txn_counts aren't used to compute throughput (is
+                         // just an informative number to print to the console
+                         // in verbose mode)
+        break;
+      }
+      d -= workload[i].frequency;
+    }
+  }
+}
+
+void
+bench_runner::run()
+{
+  // load data
+  const vector<bench_loader *> loaders = make_loaders();
+  {
+    spin_barrier b(loaders.size());
+    const pair<uint64_t, uint64_t> mem_info_before = get_system_memory_info();
+    {
+      scoped_timer t("dataloading", verbose);
+      for (vector<bench_loader *>::const_iterator it = loaders.begin();
+          it != loaders.end(); ++it) {
+        (*it)->set_barrier(b);
+        (*it)->start();
+      }
+      for (vector<bench_loader *>::const_iterator it = loaders.begin();
+          it != loaders.end(); ++it)
+        (*it)->join();
+    }
+    const pair<uint64_t, uint64_t> mem_info_after = get_system_memory_info();
+    const int64_t delta = int64_t(mem_info_before.first) - int64_t(mem_info_after.first); // free mem
+    const double delta_mb = double(delta)/1048576.0;
+    if (verbose)
+      cerr << "DB size: " << delta_mb << " MB" << endl;
+  }
+
+  db->do_txn_epoch_sync(); // also waits for worker threads to be persisted
+  {
+    const auto persisted_info = db->get_ntxn_persisted();
+    if (get<0>(persisted_info) != get<1>(persisted_info))
+      cerr << "ERROR: " << persisted_info << endl;
+    //ALWAYS_ASSERT(get<0>(persisted_info) == get<1>(persisted_info));
+    if (verbose)
+      cerr << persisted_info << " txns persisted in loading phase" << endl;
+  }
+  db->reset_ntxn_persisted();
+
+  if (!no_reset_counters) {
+    event_counter::reset_all_counters(); // XXX: for now - we really should have a before/after loading
+    PERF_EXPR(scopedperf::perfsum_base::resetall());
+  }
+  {
+    const auto persisted_info = db->get_ntxn_persisted();
+    if (get<0>(persisted_info) != 0 ||
+        get<1>(persisted_info) != 0 ||
+        get<2>(persisted_info) != 0.0) {
+      cerr << persisted_info << endl;
+      ALWAYS_ASSERT(false);
+    }
+  }
+
+  map<string, size_t> table_sizes_before;
+  if (verbose) {
+    for (map<string, abstract_ordered_index *>::iterator it = open_tables.begin();
+         it != open_tables.end(); ++it) {
+      scoped_rcu_region guard;
+      const size_t s = it->second->size();
+      cerr << "table " << it->first << " size " << s << endl;
+      table_sizes_before[it->first] = s;
+    }
+    cerr << "starting benchmark..." << endl;
+  }
+
+  const pair<uint64_t, uint64_t> mem_info_before = get_system_memory_info();
+
+  const vector<bench_worker *> workers = make_workers();
+  ALWAYS_ASSERT(!workers.empty());
+  for (vector<bench_worker *>::const_iterator it = workers.begin();
+       it != workers.end(); ++it)
+    (*it)->start();
+
+  barrier_a.wait_for(); // wait for all threads to start up
+  timer t, t_nosync;
+  barrier_b.count_down(); // bombs away!
+  if (run_mode == RUNMODE_TIME) {
+    sleep(runtime);
+    running = false;
+  }
+  __sync_synchronize();
+  for (size_t i = 0; i < nthreads; i++)
+    workers[i]->join();
+  const unsigned long elapsed_nosync = t_nosync.lap();
+  db->do_txn_finish(); // waits for all worker txns to persist
+  size_t n_commits = 0;
+  size_t n_aborts = 0;
+  uint64_t latency_numer_us = 0;
+  for (size_t i = 0; i < nthreads; i++) {
+    n_commits += workers[i]->get_ntxn_commits();
+    n_aborts += workers[i]->get_ntxn_aborts();
+    latency_numer_us += workers[i]->get_latency_numer_us();
+  }
+  const auto persisted_info = db->get_ntxn_persisted();
+
+  const unsigned long elapsed = t.lap(); // lap() must come after do_txn_finish(),
+                                         // because do_txn_finish() potentially
+                                         // waits a bit
+
+  // various sanity checks
+  ALWAYS_ASSERT(get<0>(persisted_info) == get<1>(persisted_info));
+  // not == b/c persisted_info does not count read-only txns
+  ALWAYS_ASSERT(n_commits >= get<1>(persisted_info));
+
+  const double elapsed_nosync_sec = double(elapsed_nosync) / 1000000.0;
+  const double agg_nosync_throughput = double(n_commits) / elapsed_nosync_sec;
+  const double avg_nosync_per_core_throughput = agg_nosync_throughput / double(workers.size());
+
+  const double elapsed_sec = double(elapsed) / 1000000.0;
+  const double agg_throughput = double(n_commits) / elapsed_sec;
+  const double avg_per_core_throughput = agg_throughput / double(workers.size());
+
+  const double agg_abort_rate = double(n_aborts) / elapsed_sec;
+  const double avg_per_core_abort_rate = agg_abort_rate / double(workers.size());
+
+  // we can use n_commits here, because we explicitly wait for all txns
+  // run to be durable
+  const double agg_persist_throughput = double(n_commits) / elapsed_sec;
+  const double avg_per_core_persist_throughput =
+    agg_persist_throughput / double(workers.size());
+
+  // XXX(stephentu): latency currently doesn't account for read-only txns
+  const double avg_latency_us =
+    double(latency_numer_us) / double(n_commits);
+  const double avg_latency_ms = avg_latency_us / 1000.0;
+  const double avg_persist_latency_ms =
+    get<2>(persisted_info) / 1000.0;
+
+  if (verbose) {
+    const pair<uint64_t, uint64_t> mem_info_after = get_system_memory_info();
+    const int64_t delta = int64_t(mem_info_before.first) - int64_t(mem_info_after.first); // free mem
+    const double delta_mb = double(delta)/1048576.0;
+    map<string, size_t> agg_txn_counts = workers[0]->get_txn_counts();
+    ssize_t size_delta = workers[0]->get_size_delta();
+    for (size_t i = 1; i < workers.size(); i++) {
+      map_agg(agg_txn_counts, workers[i]->get_txn_counts());
+      size_delta += workers[i]->get_size_delta();
+    }
+    const double size_delta_mb = double(size_delta)/1048576.0;
+    map<string, counter_data> ctrs = event_counter::get_all_counters();
+
+    cerr << "--- table statistics ---" << endl;
+    for (map<string, abstract_ordered_index *>::iterator it = open_tables.begin();
+         it != open_tables.end(); ++it) {
+      scoped_rcu_region guard;
+      const size_t s = it->second->size();
+      const ssize_t delta = ssize_t(s) - ssize_t(table_sizes_before[it->first]);
+      cerr << "table " << it->first << " size " << it->second->size();
+      if (delta < 0)
+        cerr << " (" << delta << " records)" << endl;
+      else
+        cerr << " (+" << delta << " records)" << endl;
+    }
+#ifdef ENABLE_BENCH_TXN_COUNTERS
+    cerr << "--- txn counter statistics ---" << endl;
+    {
+      // take from thread 0 for now
+      abstract_db::txn_counter_map agg = workers[0]->get_local_txn_counters();
+      for (auto &p : agg) {
+        cerr << p.first << ":" << endl;
+        for (auto &q : p.second)
+          cerr << "  " << q.first << " : " << q.second << endl;
+      }
+    }
+#endif
+    cerr << "--- benchmark statistics ---" << endl;
+    cerr << "runtime: " << elapsed_sec << " sec" << endl;
+    cerr << "memory delta: " << delta_mb  << " MB" << endl;
+    cerr << "memory delta rate: " << (delta_mb / elapsed_sec)  << " MB/sec" << endl;
+    cerr << "logical memory delta: " << size_delta_mb << " MB" << endl;
+    cerr << "logical memory delta rate: " << (size_delta_mb / elapsed_sec) << " MB/sec" << endl;
+    cerr << "agg_nosync_throughput: " << agg_nosync_throughput << " ops/sec" << endl;
+    cerr << "avg_nosync_per_core_throughput: " << avg_nosync_per_core_throughput << " ops/sec/core" << endl;
+    cerr << "agg_throughput: " << agg_throughput << " ops/sec" << endl;
+    cerr << "avg_per_core_throughput: " << avg_per_core_throughput << " ops/sec/core" << endl;
+    cerr << "agg_persist_throughput: " << agg_persist_throughput << " ops/sec" << endl;
+    cerr << "avg_per_core_persist_throughput: " << avg_per_core_persist_throughput << " ops/sec/core" << endl;
+    cerr << "avg_latency: " << avg_latency_ms << " ms" << endl;
+    cerr << "avg_persist_latency: " << avg_persist_latency_ms << " ms" << endl;
+    cerr << "agg_abort_rate: " << agg_abort_rate << " aborts/sec" << endl;
+    cerr << "avg_per_core_abort_rate: " << avg_per_core_abort_rate << " aborts/sec/core" << endl;
+    cerr << "txn breakdown: " << format_list(agg_txn_counts.begin(), agg_txn_counts.end()) << endl;
+    cerr << "--- system counters (for benchmark) ---" << endl;
+    for (map<string, counter_data>::iterator it = ctrs.begin();
+         it != ctrs.end(); ++it)
+      cerr << it->first << ": " << it->second << endl;
+    cerr << "--- perf counters (if enabled, for benchmark) ---" << endl;
+    PERF_EXPR(scopedperf::perfsum_base::printall());
+    cerr << "--- allocator stats ---" << endl;
+    ::allocator::DumpStats();
+    cerr << "---------------------------------------" << endl;
+
+#ifdef USE_JEMALLOC
+    cerr << "dumping heap profile..." << endl;
+    mallctl("prof.dump", NULL, NULL, NULL, 0);
+    cerr << "printing jemalloc stats..." << endl;
+    malloc_stats_print(write_cb, NULL, "");
+#endif
+#ifdef USE_TCMALLOC
+    HeapProfilerDump("before-exit");
+#endif
+  }
+
+  // output for plotting script
+  cout << agg_throughput << " "
+       << agg_persist_throughput << " "
+       << avg_latency_ms << " "
+       << avg_persist_latency_ms << " "
+       << agg_abort_rate << endl;
+  cout.flush();
+
+  if (!slow_exit)
+    return;
+
+  map<string, uint64_t> agg_stats;
+  for (map<string, abstract_ordered_index *>::iterator it = open_tables.begin();
+       it != open_tables.end(); ++it) {
+    map_agg(agg_stats, it->second->clear());
+    delete it->second;
+  }
+  if (verbose) {
+    for (auto &p : agg_stats)
+      cerr << p.first << " : " << p.second << endl;
+
+  }
+  open_tables.clear();
+
+  delete_pointers(loaders);
+  delete_pointers(workers);
+}
+
+template <typename K, typename V>
+struct map_maxer {
+  typedef map<K, V> map_type;
+  void
+  operator()(map_type &agg, const map_type &m) const
+  {
+    for (typename map_type::const_iterator it = m.begin();
+        it != m.end(); ++it)
+      agg[it->first] = std::max(agg[it->first], it->second);
+  }
+};
+
+//template <typename KOuter, typename KInner, typename VInner>
+//struct map_maxer<KOuter, map<KInner, VInner>> {
+//  typedef map<KInner, VInner> inner_map_type;
+//  typedef map<KOuter, inner_map_type> map_type;
+//};
+
+#ifdef ENABLE_BENCH_TXN_COUNTERS
+void
+bench_worker::measure_txn_counters(void *txn, const char *txn_name)
+{
+  auto ret = db->get_txn_counters(txn);
+  map_maxer<string, uint64_t>()(local_txn_counters[txn_name], ret);
+}
+#endif
+
+map<string, size_t>
+bench_worker::get_txn_counts() const
+{
+  map<string, size_t> m;
+  const workload_desc_vec workload = get_workload();
+  for (size_t i = 0; i < txn_counts.size(); i++)
+    m[workload[i].name] = txn_counts[i];
+  return m;
+}
diff --git a/silo/benchmarks/bench.h b/silo/benchmarks/bench.h
new file mode 100644 (file)
index 0000000..777cce0
--- /dev/null
@@ -0,0 +1,338 @@
+#ifndef _NDB_BENCH_H_
+#define _NDB_BENCH_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+#include <utility>
+#include <string>
+
+#include "abstract_db.h"
+#include "../macros.h"
+#include "../thread.h"
+#include "../util.h"
+#include "../spinbarrier.h"
+#include "../rcu.h"
+
+extern void ycsb_do_test(abstract_db *db, int argc, char **argv);
+extern void tpcc_do_test(abstract_db *db, int argc, char **argv);
+extern void queue_do_test(abstract_db *db, int argc, char **argv);
+extern void encstress_do_test(abstract_db *db, int argc, char **argv);
+extern void bid_do_test(abstract_db *db, int argc, char **argv);
+
+enum {
+  RUNMODE_TIME = 0,
+  RUNMODE_OPS  = 1
+};
+
+// benchmark global variables
+extern size_t nthreads;
+extern volatile bool running;
+extern int verbose;
+extern uint64_t txn_flags;
+extern double scale_factor;
+extern uint64_t runtime;
+extern uint64_t ops_per_worker;
+extern int run_mode;
+extern int enable_parallel_loading;
+extern int pin_cpus;
+extern int slow_exit;
+extern int retry_aborted_transaction;
+extern int no_reset_counters;
+extern int backoff_aborted_transaction;
+
+class scoped_db_thread_ctx {
+public:
+  scoped_db_thread_ctx(const scoped_db_thread_ctx &) = delete;
+  scoped_db_thread_ctx(scoped_db_thread_ctx &&) = delete;
+  scoped_db_thread_ctx &operator=(const scoped_db_thread_ctx &) = delete;
+
+  scoped_db_thread_ctx(abstract_db *db, bool loader)
+    : db(db)
+  {
+    db->thread_init(loader);
+  }
+  ~scoped_db_thread_ctx()
+  {
+    db->thread_end();
+  }
+private:
+  abstract_db *const db;
+};
+
+class bench_loader : public ndb_thread {
+public:
+  bench_loader(unsigned long seed, abstract_db *db,
+               const std::map<std::string, abstract_ordered_index *> &open_tables)
+    : r(seed), db(db), open_tables(open_tables), b(0)
+  {
+    txn_obj_buf.reserve(str_arena::MinStrReserveLength);
+    txn_obj_buf.resize(db->sizeof_txn_object(txn_flags));
+  }
+  inline void
+  set_barrier(spin_barrier &b)
+  {
+    ALWAYS_ASSERT(!this->b);
+    this->b = &b;
+  }
+  virtual void
+  run()
+  {
+    { // XXX(stephentu): this is a hack
+      scoped_rcu_region r; // register this thread in rcu region
+    }
+    ALWAYS_ASSERT(b);
+    b->count_down();
+    b->wait_for();
+    scoped_db_thread_ctx ctx(db, true);
+    load();
+  }
+protected:
+  inline void *txn_buf() { return (void *) txn_obj_buf.data(); }
+
+  virtual void load() = 0;
+
+  util::fast_random r;
+  abstract_db *const db;
+  std::map<std::string, abstract_ordered_index *> open_tables;
+  spin_barrier *b;
+  std::string txn_obj_buf;
+  str_arena arena;
+};
+
+class bench_worker : public ndb_thread {
+public:
+
+  bench_worker(unsigned int worker_id,
+               bool set_core_id,
+               unsigned long seed, abstract_db *db,
+               const std::map<std::string, abstract_ordered_index *> &open_tables,
+               spin_barrier *barrier_a, spin_barrier *barrier_b)
+    : worker_id(worker_id), set_core_id(set_core_id),
+      r(seed), db(db), open_tables(open_tables),
+      barrier_a(barrier_a), barrier_b(barrier_b),
+      // the ntxn_* numbers are per worker
+      ntxn_commits(0), ntxn_aborts(0),
+      latency_numer_us(0),
+      backoff_shifts(0), // spin between [0, 2^backoff_shifts) times before retry
+      size_delta(0)
+  {
+    txn_obj_buf.reserve(str_arena::MinStrReserveLength);
+    txn_obj_buf.resize(db->sizeof_txn_object(txn_flags));
+  }
+
+  virtual ~bench_worker() {}
+
+  // returns [did_commit?, size_increase_bytes]
+  typedef std::pair<bool, ssize_t> txn_result;
+  typedef txn_result (*txn_fn_t)(bench_worker *);
+
+  struct workload_desc {
+    workload_desc() {}
+    workload_desc(const std::string &name, double frequency, txn_fn_t fn)
+      : name(name), frequency(frequency), fn(fn)
+    {
+      ALWAYS_ASSERT(frequency > 0.0);
+      ALWAYS_ASSERT(frequency <= 1.0);
+    }
+    std::string name;
+    double frequency;
+    txn_fn_t fn;
+  };
+  typedef std::vector<workload_desc> workload_desc_vec;
+  virtual workload_desc_vec get_workload() const = 0;
+
+  virtual void run();
+
+  inline size_t get_ntxn_commits() const { return ntxn_commits; }
+  inline size_t get_ntxn_aborts() const { return ntxn_aborts; }
+
+  inline uint64_t get_latency_numer_us() const { return latency_numer_us; }
+
+  inline double
+  get_avg_latency_us() const
+  {
+    return double(latency_numer_us) / double(ntxn_commits);
+  }
+
+  std::map<std::string, size_t> get_txn_counts() const;
+
+  typedef abstract_db::counter_map counter_map;
+  typedef abstract_db::txn_counter_map txn_counter_map;
+
+#ifdef ENABLE_BENCH_TXN_COUNTERS
+  inline txn_counter_map
+  get_local_txn_counters() const
+  {
+    return local_txn_counters;
+  }
+#endif
+
+  inline ssize_t get_size_delta() const { return size_delta; }
+
+protected:
+
+  virtual void on_run_setup() {}
+
+  inline void *txn_buf() { return (void *) txn_obj_buf.data(); }
+
+  unsigned int worker_id;
+  bool set_core_id;
+  util::fast_random r;
+  abstract_db *const db;
+  std::map<std::string, abstract_ordered_index *> open_tables;
+  spin_barrier *const barrier_a;
+  spin_barrier *const barrier_b;
+
+private:
+  size_t ntxn_commits;
+  size_t ntxn_aborts;
+  uint64_t latency_numer_us;
+  unsigned backoff_shifts;
+
+protected:
+
+#ifdef ENABLE_BENCH_TXN_COUNTERS
+  txn_counter_map local_txn_counters;
+  void measure_txn_counters(void *txn, const char *txn_name);
+#else
+  inline ALWAYS_INLINE void measure_txn_counters(void *txn, const char *txn_name) {}
+#endif
+
+  std::vector<size_t> txn_counts; // breakdown of txns
+  ssize_t size_delta; // how many logical bytes (of values) did the worker add to the DB
+
+  std::string txn_obj_buf;
+  str_arena arena;
+};
+
+class bench_runner {
+public:
+  bench_runner(const bench_runner &) = delete;
+  bench_runner(bench_runner &&) = delete;
+  bench_runner &operator=(const bench_runner &) = delete;
+
+  bench_runner(abstract_db *db)
+    : db(db), barrier_a(nthreads), barrier_b(1) {}
+  virtual ~bench_runner() {}
+  void run();
+protected:
+  // only called once
+  virtual std::vector<bench_loader*> make_loaders() = 0;
+
+  // only called once
+  virtual std::vector<bench_worker*> make_workers() = 0;
+
+  abstract_db *const db;
+  std::map<std::string, abstract_ordered_index *> open_tables;
+
+  // barriers for actual benchmark execution
+  spin_barrier barrier_a;
+  spin_barrier barrier_b;
+};
+
+// XXX(stephentu): limit_callback is not optimal, should use
+// static_limit_callback if possible
+class limit_callback : public abstract_ordered_index::scan_callback {
+public:
+  limit_callback(ssize_t limit = -1)
+    : limit(limit), n(0)
+  {
+    ALWAYS_ASSERT(limit == -1 || limit > 0);
+  }
+
+  virtual bool invoke(
+      const char *keyp, size_t keylen,
+      const std::string &value)
+  {
+    INVARIANT(limit == -1 || n < size_t(limit));
+    values.emplace_back(std::string(keyp, keylen), value);
+    return (limit == -1) || (++n < size_t(limit));
+  }
+
+  typedef std::pair<std::string, std::string> kv_pair;
+  std::vector<kv_pair> values;
+
+  const ssize_t limit;
+private:
+  size_t n;
+};
+
+
+class latest_key_callback : public abstract_ordered_index::scan_callback {
+public:
+  latest_key_callback(std::string &k, ssize_t limit = -1)
+    : limit(limit), n(0), k(&k)
+  {
+    ALWAYS_ASSERT(limit == -1 || limit > 0);
+  }
+
+  virtual bool invoke(
+      const char *keyp, size_t keylen,
+      const std::string &value)
+  {
+    INVARIANT(limit == -1 || n < size_t(limit));
+    k->assign(keyp, keylen);
+    ++n;
+    return (limit == -1) || (n < size_t(limit));
+  }
+
+  inline size_t size() const { return n; }
+  inline std::string &kstr() { return *k; }
+
+private:
+  ssize_t limit;
+  size_t n;
+  std::string *k;
+};
+
+// explicitly copies keys, because btree::search_range_call() interally
+// re-uses a single string to pass keys (so using standard string assignment
+// will force a re-allocation b/c of shared ref-counting)
+//
+// this isn't done for values, because each value has a distinct string from
+// the string allocator, so there are no mutations while holding > 1 ref-count
+template <size_t N>
+class static_limit_callback : public abstract_ordered_index::scan_callback {
+public:
+  // XXX: push ignore_key into lower layer
+  static_limit_callback(str_arena *arena, bool ignore_key)
+    : n(0), arena(arena), ignore_key(ignore_key)
+  {
+    static_assert(N > 0, "xx");
+  }
+
+  virtual bool invoke(
+      const char *keyp, size_t keylen,
+      const std::string &value)
+  {
+    INVARIANT(n < N);
+    INVARIANT(arena->manages(&value));
+    if (ignore_key) {
+      values.emplace_back(nullptr, &value);
+    } else {
+      std::string * const s_px = arena->next();
+      INVARIANT(s_px && s_px->empty());
+      s_px->assign(keyp, keylen);
+      values.emplace_back(s_px, &value);
+    }
+    return ++n < N;
+  }
+
+  inline size_t
+  size() const
+  {
+    return values.size();
+  }
+
+  typedef std::pair<const std::string *, const std::string *> kv_pair;
+  typename util::vec<kv_pair, N>::type values;
+
+private:
+  size_t n;
+  str_arena *arena;
+  bool ignore_key;
+};
+
+#endif /* _NDB_BENCH_H_ */
diff --git a/silo/benchmarks/bid.cc b/silo/benchmarks/bid.cc
new file mode 100644 (file)
index 0000000..bddcbad
--- /dev/null
@@ -0,0 +1,266 @@
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <utility>
+#include <string>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../macros.h"
+#include "../varkey.h"
+#include "../thread.h"
+#include "../util.h"
+#include "../spinbarrier.h"
+
+#include "../record/encoder.h"
+#include "bench.h"
+
+using namespace std;
+using namespace util;
+
+static size_t nusers;
+static size_t nproducts;
+static const float pricefactor = 10000.0; // bids range from [0, 10000.0)
+
+#define BIDUSER_REC_KEY_FIELDS(x, y) \
+  x(uint32_t,uid)
+#define BIDUSER_REC_VALUE_FIELDS(x, y) \
+  x(uint32_t,bid)
+DO_STRUCT(biduser_rec, BIDUSER_REC_KEY_FIELDS, BIDUSER_REC_VALUE_FIELDS)
+
+#define BID_REC_KEY_FIELDS(x, y) \
+  x(uint32_t,uid) \
+  y(uint32_t,bid)
+#define BID_REC_VALUE_FIELDS(x, y) \
+  x(uint32_t,pid) \
+  y(float,amount)
+DO_STRUCT(bid_rec, BID_REC_KEY_FIELDS, BID_REC_VALUE_FIELDS)
+
+#define BIDMAX_REC_KEY_FIELDS(x, y) \
+  x(uint32_t,pid)
+#define BIDMAX_REC_VALUE_FIELDS(x, y) \
+  x(float,amount)
+DO_STRUCT(bidmax_rec, BIDMAX_REC_KEY_FIELDS, BIDMAX_REC_VALUE_FIELDS)
+
+class bid_worker : public bench_worker {
+public:
+  bid_worker(
+      unsigned int worker_id,
+      unsigned long seed, abstract_db *db,
+      const map<string, abstract_ordered_index *> &open_tables,
+      spin_barrier *barrier_a, spin_barrier *barrier_b)
+    : bench_worker(worker_id, false, seed, db,
+                   open_tables, barrier_a, barrier_b),
+      bidusertbl(open_tables.at("biduser")),
+      bidtbl(open_tables.at("bid")),
+      bidmaxtbl(open_tables.at("bidmax"))
+  {
+  }
+
+  txn_result
+  txn_bid()
+  {
+    void *txn = db->new_txn(txn_flags, arena, txn_buf());
+    try {
+      // pick user at random
+      biduser_rec::key biduser_key(r.next() % nusers);
+      ALWAYS_ASSERT(bidusertbl->get(txn, Encode(obj_k0, biduser_key), obj_v0));
+      biduser_rec::value biduser_value_temp;
+      const biduser_rec::value *biduser_value = Decode(obj_v0, biduser_value_temp);
+
+      // update the user's bid
+      const uint32_t bid = biduser_value->bid;
+      biduser_value_temp.bid++;
+      bidusertbl->put(txn, Encode(str(), biduser_key), Encode(str(), biduser_value_temp));
+
+      // insert the new bid
+      const bid_rec::key bid_key(biduser_key.uid, bid);
+      const bid_rec::value bid_value(r.next() % nproducts, r.next_uniform() * pricefactor);
+      bidtbl->insert(txn, Encode(str(), bid_key), Encode(str(), bid_value));
+
+      // update the max value if necessary
+      const bidmax_rec::key bidmax_key(bid_value.pid);
+      ALWAYS_ASSERT(bidmaxtbl->get(txn, Encode(obj_k0, bidmax_key), obj_v0));
+      bidmax_rec::value bidmax_value_temp;
+      const bidmax_rec::value *bidmax_value = Decode(obj_v0, bidmax_value_temp);
+
+      if (bid_value.amount > bidmax_value->amount) {
+        bidmax_value_temp.amount = bid_value.amount;
+        bidmaxtbl->put(txn, Encode(str(), bidmax_key), Encode(str(), bidmax_value_temp));
+      }
+
+      if (likely(db->commit_txn(txn)))
+        return txn_result(true, 0);
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnBid(bench_worker *w)
+  {
+    return static_cast<bid_worker *>(w)->txn_bid();
+  }
+
+  virtual workload_desc_vec
+  get_workload() const
+  {
+    workload_desc_vec w;
+    w.push_back(workload_desc("Bid", 1.0, TxnBid));
+    return w;
+  }
+
+private:
+  inline ALWAYS_INLINE string &
+  str()
+  {
+    return *arena.next();
+  }
+
+  abstract_ordered_index *bidusertbl;
+  abstract_ordered_index *bidtbl;
+  abstract_ordered_index *bidmaxtbl;
+
+  // scratch buffer space
+  string obj_k0;
+  string obj_v0;
+
+};
+
+class bid_loader : public bench_loader {
+public:
+  bid_loader(unsigned long seed,
+             abstract_db *db,
+             const map<string, abstract_ordered_index *> &open_tables)
+    : bench_loader(seed, db, open_tables)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    abstract_ordered_index *bidusertbl = open_tables.at("biduser");
+    abstract_ordered_index *bidmaxtbl = open_tables.at("bidmax");
+    try {
+      // load
+      const size_t batchsize = (db->txn_max_batch_size() == -1) ?
+        10000 : db->txn_max_batch_size();
+      ALWAYS_ASSERT(batchsize > 0);
+
+      {
+        const size_t nbatches = nusers / batchsize;
+        if (nbatches == 0) {
+          void *txn = db->new_txn(txn_flags, arena, txn_buf());
+          for (size_t j = 0; j < nusers; j++) {
+            const biduser_rec::key key(j);
+            const biduser_rec::value value(0);
+            string buf0;
+            bidusertbl->insert(txn, Encode(key), Encode(buf0, value));
+          }
+          if (verbose)
+            cerr << "batch 1/1 done" << endl;
+          ALWAYS_ASSERT(db->commit_txn(txn));
+        } else {
+          for (size_t i = 0; i < nbatches; i++) {
+            size_t keyend = (i == nbatches - 1) ? nusers : (i + 1) * batchsize;
+            void *txn = db->new_txn(txn_flags, arena, txn_buf());
+            for (size_t j = i * batchsize; j < keyend; j++) {
+              const biduser_rec::key key(j);
+              const biduser_rec::value value(0);
+              string buf0;
+              bidusertbl->insert(txn, Encode(key), Encode(buf0, value));
+            }
+            if (verbose)
+              cerr << "batch " << (i + 1) << "/" << nbatches << " done" << endl;
+            ALWAYS_ASSERT(db->commit_txn(txn));
+          }
+        }
+        if (verbose)
+          cerr << "[INFO] finished loading BIDUSER table" << endl;
+      }
+
+      {
+        const size_t nbatches = nproducts / batchsize;
+        if (nbatches == 0) {
+          void *txn = db->new_txn(txn_flags, arena, txn_buf());
+          for (size_t j = 0; j < nproducts; j++) {
+            const bidmax_rec::key key(j);
+            const bidmax_rec::value value(0.0);
+            string buf0;
+            bidmaxtbl->insert(txn, Encode(key), Encode(buf0, value));
+          }
+          if (verbose)
+            cerr << "batch 1/1 done" << endl;
+          ALWAYS_ASSERT(db->commit_txn(txn));
+        } else {
+          for (size_t i = 0; i < nbatches; i++) {
+            size_t keyend = (i == nbatches - 1) ? nproducts : (i + 1) * batchsize;
+            void *txn = db->new_txn(txn_flags, arena, txn_buf());
+            for (size_t j = i * batchsize; j < keyend; j++) {
+              const bidmax_rec::key key(j);
+              const bidmax_rec::value value(0.0);
+              string buf0;
+              bidmaxtbl->insert(txn, Encode(key), Encode(buf0, value));
+            }
+            if (verbose)
+              cerr << "batch " << (i + 1) << "/" << nbatches << " done" << endl;
+            ALWAYS_ASSERT(db->commit_txn(txn));
+          }
+        }
+        if (verbose)
+          cerr << "[INFO] finished loading BIDMAX table" << endl;
+      }
+
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      // shouldn't abort on loading!
+      ALWAYS_ASSERT(false);
+    }
+
+  }
+};
+
+class bid_bench_runner : public bench_runner {
+public:
+  bid_bench_runner(abstract_db *db)
+    : bench_runner(db)
+  {
+    open_tables["biduser"] = db->open_index("biduser", sizeof(biduser_rec));
+    open_tables["bid"] = db->open_index("bid", sizeof(bid_rec));
+    open_tables["bidmax"] = db->open_index("bidmax", sizeof(bidmax_rec));
+  }
+
+protected:
+  virtual vector<bench_loader *>
+  make_loaders()
+  {
+    vector<bench_loader *> ret;
+    ret.push_back(new bid_loader(0, db, open_tables));
+    return ret;
+  }
+
+  virtual vector<bench_worker *>
+  make_workers()
+  {
+    fast_random r(36578943);
+    vector<bench_worker *> ret;
+    for (size_t i = 0; i < nthreads; i++)
+      ret.push_back(
+        new bid_worker(
+          i, r.next(), db, open_tables,
+          &barrier_a, &barrier_b));
+    return ret;
+  }
+};
+
+void
+bid_do_test(abstract_db *db, int argc, char **argv)
+{
+  nusers = size_t(scale_factor * 1000.0);
+  nproducts = size_t(scale_factor * 1000.0);
+  ALWAYS_ASSERT(nusers > 0);
+  ALWAYS_ASSERT(nproducts > 0);
+  bid_bench_runner r(db);
+  r.run();
+}
diff --git a/silo/benchmarks/dbtest.cc b/silo/benchmarks/dbtest.cc
new file mode 100644 (file)
index 0000000..0605654
--- /dev/null
@@ -0,0 +1,407 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <utility>
+#include <string>
+#include <set>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#include "../allocator.h"
+#include "../stats_server.h"
+#include "bench.h"
+#include "bdb_wrapper.h"
+#include "ndb_wrapper.h"
+#include "ndb_wrapper_impl.h"
+#include "kvdb_wrapper.h"
+#include "kvdb_wrapper_impl.h"
+#if !NO_MYSQL
+#include "mysql_wrapper.h"
+#endif
+
+using namespace std;
+using namespace util;
+
+static vector<string>
+split_ws(const string &s)
+{
+  vector<string> r;
+  istringstream iss(s);
+  copy(istream_iterator<string>(iss),
+       istream_iterator<string>(),
+       back_inserter<vector<string>>(r));
+  return r;
+}
+
+static size_t
+parse_memory_spec(const string &s)
+{
+  string x(s);
+  size_t mult = 1;
+  if (x.back() == 'G') {
+    mult = static_cast<size_t>(1) << 30;
+    x.pop_back();
+  } else if (x.back() == 'M') {
+    mult = static_cast<size_t>(1) << 20;
+    x.pop_back();
+  } else if (x.back() == 'K') {
+    mult = static_cast<size_t>(1) << 10;
+    x.pop_back();
+  }
+  return strtoul(x.c_str(), nullptr, 10) * mult;
+}
+
+int
+main(int argc, char **argv)
+{
+  abstract_db *db = NULL;
+  void (*test_fn)(abstract_db *, int argc, char **argv) = NULL;
+  string bench_type = "ycsb";
+  string db_type = "ndb-proto2";
+  char *curdir = get_current_dir_name();
+  string basedir = curdir;
+  string bench_opts;
+  size_t numa_memory = 0;
+  free(curdir);
+  int saw_run_spec = 0;
+  int nofsync = 0;
+  int do_compress = 0;
+  int fake_writes = 0;
+  int disable_gc = 0;
+  int disable_snapshots = 0;
+  vector<string> logfiles;
+  vector<vector<unsigned>> assignments;
+  string stats_server_sockfile;
+  while (1) {
+    static struct option long_options[] =
+    {
+      {"verbose"                    , no_argument       , &verbose                   , 1}   ,
+      {"parallel-loading"           , no_argument       , &enable_parallel_loading   , 1}   ,
+      {"pin-cpus"                   , no_argument       , &pin_cpus                  , 1}   ,
+      {"slow-exit"                  , no_argument       , &slow_exit                 , 1}   ,
+      {"retry-aborted-transactions" , no_argument       , &retry_aborted_transaction , 1}   ,
+      {"backoff-aborted-transactions" , no_argument     , &backoff_aborted_transaction , 1}   ,
+      {"bench"                      , required_argument , 0                          , 'b'} ,
+      {"scale-factor"               , required_argument , 0                          , 's'} ,
+      {"num-threads"                , required_argument , 0                          , 't'} ,
+      {"db-type"                    , required_argument , 0                          , 'd'} ,
+      {"basedir"                    , required_argument , 0                          , 'B'} ,
+      {"txn-flags"                  , required_argument , 0                          , 'f'} ,
+      {"runtime"                    , required_argument , 0                          , 'r'} ,
+      {"ops-per-worker"             , required_argument , 0                          , 'n'} ,
+      {"bench-opts"                 , required_argument , 0                          , 'o'} ,
+      {"numa-memory"                , required_argument , 0                          , 'm'} , // implies --pin-cpus
+      {"logfile"                    , required_argument , 0                          , 'l'} ,
+      {"assignment"                 , required_argument , 0                          , 'a'} ,
+      {"log-nofsync"                , no_argument       , &nofsync                   , 1}   ,
+      {"log-compress"               , no_argument       , &do_compress               , 1}   ,
+      {"log-fake-writes"            , no_argument       , &fake_writes               , 1}   ,
+      {"disable-gc"                 , no_argument       , &disable_gc                , 1}   ,
+      {"disable-snapshots"          , no_argument       , &disable_snapshots         , 1}   ,
+      {"stats-server-sockfile"      , required_argument , 0                          , 'x'} ,
+      {"no-reset-counters"          , no_argument       , &no_reset_counters         , 1}   ,
+      {0, 0, 0, 0}
+    };
+    int option_index = 0;
+    int c = getopt_long(argc, argv, "b:s:t:d:B:f:r:n:o:m:l:a:x:", long_options, &option_index);
+    if (c == -1)
+      break;
+
+    switch (c) {
+    case 0:
+      if (long_options[option_index].flag != 0)
+        break;
+      abort();
+      break;
+
+    case 'b':
+      bench_type = optarg;
+      break;
+
+    case 's':
+      scale_factor = strtod(optarg, NULL);
+      ALWAYS_ASSERT(scale_factor > 0.0);
+      break;
+
+    case 't':
+      nthreads = strtoul(optarg, NULL, 10);
+      ALWAYS_ASSERT(nthreads > 0);
+      break;
+
+    case 'd':
+      db_type = optarg;
+      break;
+
+    case 'B':
+      basedir = optarg;
+      break;
+
+    case 'f':
+      txn_flags = strtoul(optarg, NULL, 10);
+      break;
+
+    case 'r':
+      ALWAYS_ASSERT(!saw_run_spec);
+      saw_run_spec = 1;
+      runtime = strtoul(optarg, NULL, 10);
+      ALWAYS_ASSERT(runtime > 0);
+      run_mode = RUNMODE_TIME;
+      break;
+
+    case 'n':
+      ALWAYS_ASSERT(!saw_run_spec);
+      saw_run_spec = 1;
+      ops_per_worker = strtoul(optarg, NULL, 10);
+      ALWAYS_ASSERT(ops_per_worker > 0);
+      run_mode = RUNMODE_OPS;
+
+    case 'o':
+      bench_opts = optarg;
+      break;
+
+    case 'm':
+      {
+        pin_cpus = 1;
+        const size_t m = parse_memory_spec(optarg);
+        ALWAYS_ASSERT(m > 0);
+        numa_memory = m;
+      }
+      break;
+
+    case 'l':
+      logfiles.emplace_back(optarg);
+      break;
+
+    case 'a':
+      assignments.emplace_back(
+          ParseCSVString<unsigned, RangeAwareParser<unsigned>>(optarg));
+      break;
+
+    case 'x':
+      stats_server_sockfile = optarg;
+      break;
+
+    case '?':
+      /* getopt_long already printed an error message. */
+      exit(1);
+
+    default:
+      abort();
+    }
+  }
+
+  if (bench_type == "ycsb")
+    test_fn = ycsb_do_test;
+  else if (bench_type == "tpcc")
+    test_fn = tpcc_do_test;
+  else if (bench_type == "queue")
+    test_fn = queue_do_test;
+  else if (bench_type == "encstress")
+    test_fn = encstress_do_test;
+  else if (bench_type == "bid")
+    test_fn = bid_do_test;
+  else
+    ALWAYS_ASSERT(false);
+
+  if (do_compress && logfiles.empty()) {
+    cerr << "[ERROR] --log-compress specified without logging enabled" << endl;
+    return 1;
+  }
+
+  if (fake_writes && logfiles.empty()) {
+    cerr << "[ERROR] --log-fake-writes specified without logging enabled" << endl;
+    return 1;
+  }
+
+  if (nofsync && logfiles.empty()) {
+    cerr << "[ERROR] --log-nofsync specified without logging enabled" << endl;
+    return 1;
+  }
+
+  if (fake_writes && nofsync) {
+    cerr << "[WARNING] --log-nofsync has no effect with --log-fake-writes enabled" << endl;
+  }
+
+#ifndef ENABLE_EVENT_COUNTERS
+  if (!stats_server_sockfile.empty()) {
+    cerr << "[WARNING] --stats-server-sockfile with no event counters enabled is useless" << endl;
+  }
+#endif
+
+  // initialize the numa allocator
+  if (numa_memory > 0) {
+    const size_t maxpercpu = util::iceil(
+        numa_memory / nthreads, ::allocator::GetHugepageSize());
+    numa_memory = maxpercpu * nthreads;
+    ::allocator::Initialize(nthreads, maxpercpu);
+  }
+
+  const set<string> can_persist({"ndb-proto2"});
+  if (!logfiles.empty() && !can_persist.count(db_type)) {
+    cerr << "[ERROR] benchmark " << db_type
+         << " does not have persistence implemented" << endl;
+    return 1;
+  }
+
+#ifdef PROTO2_CAN_DISABLE_GC
+  const set<string> has_gc({"ndb-proto1", "ndb-proto2"});
+  if (disable_gc && !has_gc.count(db_type)) {
+    cerr << "[ERROR] benchmark " << db_type
+         << " does not have gc to disable" << endl;
+    return 1;
+  }
+#else
+  if (disable_gc) {
+    cerr << "[ERROR] macro PROTO2_CAN_DISABLE_GC was not set, cannot disable gc" << endl;
+    return 1;
+  }
+#endif
+
+#ifdef PROTO2_CAN_DISABLE_SNAPSHOTS
+  const set<string> has_snapshots({"ndb-proto2"});
+  if (disable_snapshots && !has_snapshots.count(db_type)) {
+    cerr << "[ERROR] benchmark " << db_type
+         << " does not have snapshots to disable" << endl;
+    return 1;
+  }
+#else
+  if (disable_snapshots) {
+    cerr << "[ERROR] macro PROTO2_CAN_DISABLE_SNAPSHOTS was not set, cannot disable snapshots" << endl;
+    return 1;
+  }
+#endif
+
+  if (db_type == "bdb") {
+    const string cmd = "rm -rf " + basedir + "/db/*";
+    // XXX(stephentu): laziness
+    int ret UNUSED = system(cmd.c_str());
+    db = new bdb_wrapper("db", bench_type + ".db");
+  } else if (db_type == "ndb-proto1") {
+    // XXX: hacky simulation of proto1
+    db = new ndb_wrapper<transaction_proto2>(
+        logfiles, assignments, !nofsync, do_compress, fake_writes);
+    transaction_proto2_static::set_hack_status(true);
+    ALWAYS_ASSERT(transaction_proto2_static::get_hack_status());
+#ifdef PROTO2_CAN_DISABLE_GC
+    if (!disable_gc)
+      transaction_proto2_static::InitGC();
+#endif
+  } else if (db_type == "ndb-proto2") {
+    db = new ndb_wrapper<transaction_proto2>(
+        logfiles, assignments, !nofsync, do_compress, fake_writes);
+    ALWAYS_ASSERT(!transaction_proto2_static::get_hack_status());
+#ifdef PROTO2_CAN_DISABLE_GC
+    if (!disable_gc)
+      transaction_proto2_static::InitGC();
+#endif
+#ifdef PROTO2_CAN_DISABLE_SNAPSHOTS
+    if (disable_snapshots)
+      transaction_proto2_static::DisableSnapshots();
+#endif
+  } else if (db_type == "kvdb") {
+    db = new kvdb_wrapper<true>;
+  } else if (db_type == "kvdb-st") {
+    db = new kvdb_wrapper<false>;
+#if !NO_MYSQL
+  } else if (db_type == "mysql") {
+    string dbdir = basedir + "/mysql-db";
+    db = new mysql_wrapper(dbdir, bench_type);
+#endif
+  } else
+    ALWAYS_ASSERT(false);
+
+#ifdef DEBUG
+  cerr << "WARNING: benchmark built in DEBUG mode!!!" << endl;
+#endif
+
+#ifdef CHECK_INVARIANTS
+  cerr << "WARNING: invariant checking is enabled - should disable for benchmark" << endl;
+#ifdef PARANOID_CHECKING
+  cerr << "  *** Paranoid checking is enabled ***" << endl;
+#endif
+#endif
+
+  if (verbose) {
+    const unsigned long ncpus = coreid::num_cpus_online();
+    cerr << "Database Benchmark:"                           << endl;
+    cerr << "  pid: " << getpid()                           << endl;
+    cerr << "settings:"                                     << endl;
+    cerr << "  par-loading : " << enable_parallel_loading   << endl;
+    cerr << "  pin-cpus    : " << pin_cpus                  << endl;
+    cerr << "  slow-exit   : " << slow_exit                 << endl;
+    cerr << "  retry-txns  : " << retry_aborted_transaction << endl;
+    cerr << "  backoff-txns: " << backoff_aborted_transaction << endl;
+    cerr << "  bench       : " << bench_type                << endl;
+    cerr << "  scale       : " << scale_factor              << endl;
+    cerr << "  num-cpus    : " << ncpus                     << endl;
+    cerr << "  num-threads : " << nthreads                  << endl;
+    cerr << "  db-type     : " << db_type                   << endl;
+    cerr << "  basedir     : " << basedir                   << endl;
+    cerr << "  txn-flags   : " << hexify(txn_flags)         << endl;
+    if (run_mode == RUNMODE_TIME)
+      cerr << "  runtime     : " << runtime                 << endl;
+    else
+      cerr << "  ops/worker  : " << ops_per_worker          << endl;
+#ifdef USE_VARINT_ENCODING
+    cerr << "  var-encode  : yes"                           << endl;
+#else
+    cerr << "  var-encode  : no"                            << endl;
+#endif
+
+#ifdef USE_JEMALLOC
+    cerr << "  allocator   : jemalloc"                      << endl;
+#elif defined USE_TCMALLOC
+    cerr << "  allocator   : tcmalloc"                      << endl;
+#elif defined USE_FLOW
+    cerr << "  allocator   : flow"                          << endl;
+#else
+    cerr << "  allocator   : libc"                          << endl;
+#endif
+    if (numa_memory > 0) {
+      cerr << "  numa-memory : " << numa_memory             << endl;
+    } else {
+      cerr << "  numa-memory : disabled"                    << endl;
+    }
+    cerr << "  logfiles : " << logfiles                     << endl;
+    cerr << "  assignments : " << assignments               << endl;
+    cerr << "  disable-gc : " << disable_gc                 << endl;
+    cerr << "  disable-snapshots : " << disable_snapshots   << endl;
+    cerr << "  stats-server-sockfile: " << stats_server_sockfile << endl;
+
+    cerr << "system properties:" << endl;
+    cerr << "  btree_internal_node_size: " << concurrent_btree::InternalNodeSize() << endl;
+    cerr << "  btree_leaf_node_size    : " << concurrent_btree::LeafNodeSize() << endl;
+
+#ifdef TUPLE_PREFETCH
+    cerr << "  tuple_prefetch          : yes" << endl;
+#else
+    cerr << "  tuple_prefetch          : no" << endl;
+#endif
+
+#ifdef BTREE_NODE_PREFETCH
+    cerr << "  btree_node_prefetch     : yes" << endl;
+#else
+    cerr << "  btree_node_prefetch     : no" << endl;
+#endif
+
+  }
+
+  if (!stats_server_sockfile.empty()) {
+    stats_server *srvr = new stats_server(stats_server_sockfile);
+    thread(&stats_server::serve_forever, srvr).detach();
+  }
+
+  vector<string> bench_toks = split_ws(bench_opts);
+  int argc = 1 + bench_toks.size();
+  char *argv[argc];
+  argv[0] = (char *) bench_type.c_str();
+  for (size_t i = 1; i <= bench_toks.size(); i++)
+    argv[i] = (char *) bench_toks[i - 1].c_str();
+  test_fn(db, argc, argv);
+  delete db;
+  return 0;
+}
diff --git a/silo/benchmarks/encstress.cc b/silo/benchmarks/encstress.cc
new file mode 100644 (file)
index 0000000..7b5577a
--- /dev/null
@@ -0,0 +1,180 @@
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <utility>
+#include <string>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../macros.h"
+#include "../varkey.h"
+#include "../thread.h"
+#include "../util.h"
+#include "../spinbarrier.h"
+
+#include "../record/encoder.h"
+#include "bench.h"
+
+using namespace std;
+using namespace util;
+
+static size_t nkeys;
+
+#define ENCSTRESS_REC_KEY_FIELDS(x, y) \
+  x(int32_t,k0)
+#define ENCSTRESS_REC_VALUE_FIELDS(x, y) \
+  x(int32_t,f0) \
+  y(int32_t,f1) \
+  y(int32_t,f2) \
+  y(int32_t,f3) \
+  y(int32_t,f4) \
+  y(int32_t,f5) \
+  y(int32_t,f6) \
+  y(int32_t,f7)
+DO_STRUCT(encstress_rec, ENCSTRESS_REC_KEY_FIELDS, ENCSTRESS_REC_VALUE_FIELDS)
+
+class encstress_worker : public bench_worker {
+public:
+  encstress_worker(
+      unsigned int worker_id,
+      unsigned long seed, abstract_db *db,
+      const map<string, abstract_ordered_index *> &open_tables,
+      spin_barrier *barrier_a, spin_barrier *barrier_b)
+    : bench_worker(worker_id, false, seed, db,
+                   open_tables, barrier_a, barrier_b),
+      tbl(open_tables.at("table"))
+  {
+  }
+
+  txn_result
+  txn_read()
+  {
+    void *txn = db->new_txn(txn_flags, arena, txn_buf());
+    const string k = u64_varkey(r.next() % nkeys).str();
+    try {
+      string v;
+      ALWAYS_ASSERT(tbl->get(txn, k, v));
+      if (likely(db->commit_txn(txn)))
+        return txn_result(true, 0);
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnRead(bench_worker *w)
+  {
+    return static_cast<encstress_worker *>(w)->txn_read();
+  }
+
+  virtual workload_desc_vec
+  get_workload() const
+  {
+    workload_desc_vec w;
+    w.push_back(workload_desc("Read", 1.0, TxnRead));
+    return w;
+  }
+
+private:
+  abstract_ordered_index *tbl;
+};
+
+class encstress_loader : public bench_loader {
+public:
+  encstress_loader(unsigned long seed,
+                   abstract_db *db,
+                   const map<string, abstract_ordered_index *> &open_tables)
+    : bench_loader(seed, db, open_tables)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    abstract_ordered_index *tbl = open_tables.at("table");
+    try {
+      // load
+      const size_t batchsize = (db->txn_max_batch_size() == -1) ?
+        10000 : db->txn_max_batch_size();
+      ALWAYS_ASSERT(batchsize > 0);
+      const size_t nbatches = nkeys / batchsize;
+      if (nbatches == 0) {
+        void *txn = db->new_txn(txn_flags, arena, txn_buf());
+        for (size_t j = 0; j < nkeys; j++) {
+          const encstress_rec::key key(j);
+          encstress_rec::value rec;
+          rec.f0 = 1; rec.f1 = 1; rec.f2 = 1; rec.f3 = 1;
+          rec.f4 = 1; rec.f5 = 1; rec.f6 = 1; rec.f7 = 1;
+          string buf;
+          tbl->insert(txn, Encode(key), Encode(buf, rec));
+        }
+        if (verbose)
+          cerr << "batch 1/1 done" << endl;
+        ALWAYS_ASSERT(db->commit_txn(txn));
+      } else {
+        for (size_t i = 0; i < nbatches; i++) {
+          size_t keyend = (i == nbatches - 1) ? nkeys : (i + 1) * batchsize;
+          void *txn = db->new_txn(txn_flags, arena, txn_buf());
+          for (size_t j = i * batchsize; j < keyend; j++) {
+            const encstress_rec::key key(j);
+            encstress_rec::value rec;
+            rec.f0 = 1; rec.f1 = 1; rec.f2 = 1; rec.f3 = 1;
+            rec.f4 = 1; rec.f5 = 1; rec.f6 = 1; rec.f7 = 1;
+            string buf;
+            tbl->insert(txn, Encode(key), Encode(buf, rec));
+          }
+          if (verbose)
+            cerr << "batch " << (i + 1) << "/" << nbatches << " done" << endl;
+          ALWAYS_ASSERT(db->commit_txn(txn));
+        }
+      }
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      // shouldn't abort on loading!
+      ALWAYS_ASSERT(false);
+    }
+    if (verbose)
+      cerr << "[INFO] finished loading USERTABLE" << endl;
+  }
+};
+
+class encstress_bench_runner : public bench_runner {
+public:
+  encstress_bench_runner(abstract_db *db)
+    : bench_runner(db)
+  {
+    open_tables["table"] = db->open_index("table", sizeof(encstress_rec));
+  }
+
+protected:
+  virtual vector<bench_loader *>
+  make_loaders()
+  {
+    vector<bench_loader *> ret;
+    ret.push_back(new encstress_loader(0, db, open_tables));
+    return ret;
+  }
+
+  virtual vector<bench_worker *>
+  make_workers()
+  {
+    fast_random r(8544290);
+    vector<bench_worker *> ret;
+    for (size_t i = 0; i < nthreads; i++)
+      ret.push_back(
+        new encstress_worker(
+          i, r.next(), db, open_tables,
+          &barrier_a, &barrier_b));
+    return ret;
+  }
+};
+
+void
+encstress_do_test(abstract_db *db, int argc, char **argv)
+{
+  nkeys = size_t(scale_factor * 1000.0);
+  ALWAYS_ASSERT(nkeys > 0);
+  encstress_bench_runner r(db);
+  r.run();
+}
diff --git a/silo/benchmarks/gc_runner.sh b/silo/benchmarks/gc_runner.sh
new file mode 100755 (executable)
index 0000000..3b5aa42
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+set -x
+
+BENCH=./dbtest
+NTHREADS=28
+PREFIX=$1
+YCSB_MEM=`python -c "print int(100+1.4*$NTHREADS)"`
+
+$BENCH \
+  --verbose \
+  --bench ycsb \
+  --db-type ndb-proto2 \
+  --scale-factor 320000 \
+  --num-threads $NTHREADS \
+  --bench-opts '--workload-mix 80,0,20,0' \
+  --numa-memory ${YCSB_MEM}G \
+  --parallel-loading \
+  --runtime 60 \
+  --slow-exit 2>&1 | grep chain_ > results/$PREFIX-ycsb-chains.txt
+
+$BENCH \
+  --verbose \
+  --bench tpcc \
+  --db-type ndb-proto2 \
+  --scale-factor $NTHREADS \
+  --num-threads $NTHREADS \
+  --numa-memory $((4 * $NTHREADS))G \
+  --parallel-loading \
+  --runtime 60 \
+  --slow-exit 2>&1 | grep chain_ > results/$PREFIX-tpcc-chains.txt
diff --git a/silo/benchmarks/kvdb_wrapper.h b/silo/benchmarks/kvdb_wrapper.h
new file mode 100644 (file)
index 0000000..b2a4fdd
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef _KVDB_WRAPPER_H_
+#define _KVDB_WRAPPER_H_
+
+#include "abstract_db.h"
+#include "../btree_choice.h"
+#include "../rcu.h"
+
+template <bool UseConcurrencyControl>
+class kvdb_wrapper : public abstract_db {
+public:
+
+  virtual ssize_t txn_max_batch_size() const OVERRIDE { return 100; }
+
+  virtual void do_txn_epoch_sync() const { }
+
+  virtual void do_txn_finish() const { }
+
+  virtual size_t
+  sizeof_txn_object(uint64_t txn_flags) const
+  {
+    return sizeof(scoped_rcu_region);
+  }
+
+  virtual void *
+  new_txn(uint64_t txn_flags, str_arena &arena, void *buf, TxnProfileHint hint)
+  {
+    return new (buf) scoped_rcu_region;
+  }
+
+  virtual bool
+  commit_txn(void *txn)
+  {
+    ((scoped_rcu_region *) txn)->~scoped_rcu_region();
+    return true;
+  }
+
+  virtual void abort_txn(void *txn) { ALWAYS_ASSERT(false); } // txn should never abort
+  virtual void print_txn_debug(void *txn) const { }
+
+  virtual abstract_ordered_index *
+  open_index(const std::string &name,
+             size_t value_size_hint,
+             bool mostly_append);
+
+  virtual void
+  close_index(abstract_ordered_index *idx)
+  {
+    delete idx;
+  }
+};
+
+template <bool UseConcurrencyControl>
+class kvdb_ordered_index : public abstract_ordered_index {
+public:
+  kvdb_ordered_index(const std::string &name)
+    : name(name) {}
+  virtual bool get(
+      void *txn,
+      const std::string &key,
+      std::string &value, size_t max_bytes_read);
+  virtual const char * put(
+      void *txn,
+      const std::string &key,
+      const std::string &value);
+  virtual const char *
+  insert(void *txn,
+         const std::string &key,
+         const std::string &value);
+  virtual void scan(
+      void *txn,
+      const std::string &start_key,
+      const std::string *end_key,
+      scan_callback &callback,
+      str_arena *arena);
+  virtual void rscan(
+      void *txn,
+      const std::string &start_key,
+      const std::string *end_key,
+      scan_callback &callback,
+      str_arena *arena);
+  virtual void remove(
+      void *txn,
+      const std::string &key);
+  virtual size_t size() const;
+  virtual std::map<std::string, uint64_t> clear();
+private:
+  std::string name;
+  typedef
+    typename std::conditional<
+      UseConcurrencyControl,
+      concurrent_btree,
+      single_threaded_btree>::type
+    my_btree;
+  typedef typename my_btree::key_type key_type;
+  my_btree btr;
+};
+
+#endif /* _KVDB_WRAPPER_H_ */
diff --git a/silo/benchmarks/kvdb_wrapper_impl.h b/silo/benchmarks/kvdb_wrapper_impl.h
new file mode 100644 (file)
index 0000000..315ea88
--- /dev/null
@@ -0,0 +1,558 @@
+#ifndef _KVDB_WRAPPER_IMPL_H_
+#define _KVDB_WRAPPER_IMPL_H_
+
+#include <vector>
+#include <limits>
+#include <utility>
+
+#include "kvdb_wrapper.h"
+#include "../varint.h"
+#include "../macros.h"
+#include "../util.h"
+#include "../amd64.h"
+#include "../lockguard.h"
+#include "../prefetch.h"
+#include "../scopedperf.hh"
+#include "../counter.h"
+
+namespace private_ {
+  static event_avg_counter evt_avg_kvdb_stable_version_spins("avg_kvdb_stable_version_spins");
+  static event_avg_counter evt_avg_kvdb_lock_acquire_spins("avg_kvdb_lock_acquire_spins");
+  static event_avg_counter evt_avg_kvdb_read_retries("avg_kvdb_read_retries");
+
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_get_probe0, kvdb_get_probe0_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_get_probe1, kvdb_get_probe1_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_put_probe0, kvdb_put_probe0_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_insert_probe0, kvdb_insert_probe0_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_scan_probe0, kvdb_scan_probe0_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_remove_probe0, kvdb_remove_probe0_cg);
+}
+
+// defines single-threaded version
+template <bool UseConcurrencyControl>
+struct record_version {
+  uint16_t sz;
+
+  inline ALWAYS_INLINE bool
+  is_locked() const
+  {
+    return false;
+  }
+
+  inline ALWAYS_INLINE void lock() {}
+
+  inline ALWAYS_INLINE void unlock() {}
+
+  static inline ALWAYS_INLINE size_t
+  Size(uint32_t v)
+  {
+    return 0;
+  }
+
+  inline ALWAYS_INLINE size_t
+  size() const
+  {
+    return sz;
+  }
+
+  inline ALWAYS_INLINE void
+  set_size(size_t s)
+  {
+    INVARIANT(s <= std::numeric_limits<uint16_t>::max());
+    sz = s;
+  }
+
+  inline ALWAYS_INLINE uint32_t
+  stable_version() const
+  {
+    return 0;
+  }
+
+  inline ALWAYS_INLINE bool
+  check_version(uint32_t version) const
+  {
+    return true;
+  }
+};
+
+// concurrency control version
+template <>
+struct record_version<true> {
+  // [ locked | size  | version ]
+  // [  0..1  | 1..17 | 17..32  ]
+
+  static const uint32_t HDR_LOCKED_MASK = 0x1;
+
+  static const uint32_t HDR_SIZE_SHIFT = 1;
+  static const uint32_t HDR_SIZE_MASK = std::numeric_limits<uint16_t>::max() << HDR_SIZE_SHIFT;
+
+  static const uint32_t HDR_VERSION_SHIFT = 17;
+  static const uint32_t HDR_VERSION_MASK = ((uint32_t)-1) << HDR_VERSION_SHIFT;
+
+  record_version<true>() : hdr(0) {}
+
+  volatile uint32_t hdr;
+
+  static inline bool
+  IsLocked(uint32_t v)
+  {
+    return v & HDR_LOCKED_MASK;
+  }
+
+  inline bool
+  is_locked() const
+  {
+    return IsLocked(hdr);
+  }
+
+  inline void
+  lock()
+  {
+#ifdef ENABLE_EVENT_COUNTERS
+    unsigned long nspins = 0;
+#endif
+    uint32_t v = hdr;
+    while (IsLocked(v) ||
+           !__sync_bool_compare_and_swap(&hdr, v, v | HDR_LOCKED_MASK)) {
+      nop_pause();
+      v = hdr;
+#ifdef ENABLE_EVENT_COUNTERS
+      ++nspins;
+#endif
+    }
+    COMPILER_MEMORY_FENCE;
+#ifdef ENABLE_EVENT_COUNTERS
+    private_::evt_avg_kvdb_lock_acquire_spins.offer(nspins);
+#endif
+  }
+
+  inline void
+  unlock()
+  {
+    uint32_t v = hdr;
+    INVARIANT(IsLocked(v));
+    const uint32_t n = Version(v);
+    v &= ~HDR_VERSION_MASK;
+    v |= (((n + 1) << HDR_VERSION_SHIFT) & HDR_VERSION_MASK);
+    v &= ~HDR_LOCKED_MASK;
+    INVARIANT(!IsLocked(v));
+    COMPILER_MEMORY_FENCE;
+    hdr = v;
+  }
+
+  static inline size_t
+  Size(uint32_t v)
+  {
+    return (v & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT;
+  }
+
+  inline size_t
+  size() const
+  {
+    return Size(hdr);
+  }
+
+  inline void
+  set_size(size_t s)
+  {
+    INVARIANT(s <= std::numeric_limits<uint16_t>::max());
+    INVARIANT(is_locked());
+    const uint16_t new_sz = static_cast<uint16_t>(s);
+    hdr &= ~HDR_SIZE_MASK;
+    hdr |= (new_sz << HDR_SIZE_SHIFT);
+    INVARIANT(size() == s);
+  }
+
+  static inline uint32_t
+  Version(uint32_t v)
+  {
+    return (v & HDR_VERSION_MASK) >> HDR_VERSION_SHIFT;
+  }
+
+  inline uint32_t
+  stable_version() const
+  {
+    uint32_t v = hdr;
+#ifdef ENABLE_EVENT_COUNTERS
+    unsigned long nspins = 0;
+#endif
+    while (IsLocked(v)) {
+      nop_pause();
+      v = hdr;
+#ifdef ENABLE_EVENT_COUNTERS
+      ++nspins;
+#endif
+    }
+    COMPILER_MEMORY_FENCE;
+#ifdef ENABLE_EVENT_COUNTERS
+    private_::evt_avg_kvdb_stable_version_spins.offer(nspins);
+#endif
+    return v;
+  }
+
+  inline bool
+  check_version(uint32_t version) const
+  {
+    COMPILER_MEMORY_FENCE;
+    return hdr == version;
+  }
+};
+
+template <bool UseConcurrencyControl>
+struct basic_kvdb_record : public record_version<UseConcurrencyControl> {
+  typedef record_version<UseConcurrencyControl> super_type;
+  uint16_t alloc_size;
+  char data[0];
+
+  basic_kvdb_record(uint16_t alloc_size, const std::string &s)
+    : record_version<UseConcurrencyControl>(),
+      alloc_size(alloc_size)
+  {
+#ifdef CHECK_INVARIANTS
+    this->lock();
+    this->set_size(s.size());
+    do_write(s);
+    this->unlock();
+#else
+    this->set_size(s.size());
+    do_write(s);
+#endif
+  }
+
+  inline void
+  prefetch() const
+  {
+#ifdef PREFETCH
+    prefetch_bytes(this, sizeof(*this) + size());
+#endif
+  }
+
+  inline void
+  do_read(std::string &s, size_t max_bytes_read) const
+  {
+    if (UseConcurrencyControl) {
+#ifdef ENABLE_EVENT_COUNTERS
+      unsigned long nretries = 0;
+#endif
+    retry:
+      const uint32_t v = this->stable_version();
+      const size_t sz = std::min(super_type::Size(v), max_bytes_read);
+      s.assign(&data[0], sz);
+      if (unlikely(!this->check_version(v))) {
+#ifdef ENABLE_EVENT_COUNTERS
+        ++nretries;
+#endif
+        goto retry;
+      }
+#ifdef ENABLE_EVENT_COUNTERS
+      private_::evt_avg_kvdb_read_retries.offer(nretries);
+#endif
+    } else {
+      const size_t sz = std::min(this->size(), max_bytes_read);
+      s.assign(&data[0], sz);
+    }
+  }
+
+  inline bool
+  do_write(const std::string &s)
+  {
+    INVARIANT(!UseConcurrencyControl || this->is_locked());
+    if (unlikely(s.size() > alloc_size))
+      return false;
+    this->set_size(s.size());
+    NDB_MEMCPY(&data[0], s.data(), s.size());
+    return true;
+  }
+
+  static basic_kvdb_record *
+  alloc(const std::string &s)
+  {
+    const size_t sz = s.size();
+    const size_t max_alloc_sz =
+      std::numeric_limits<uint16_t>::max() + sizeof(basic_kvdb_record);
+    const size_t alloc_sz =
+      std::min(
+          util::round_up<size_t, allocator::LgAllocAlignment>(sizeof(basic_kvdb_record) + sz),
+          max_alloc_sz);
+    char * const p = reinterpret_cast<char *>(rcu::s_instance.alloc(alloc_sz));
+    INVARIANT(p);
+    return new (p) basic_kvdb_record(alloc_sz - sizeof(basic_kvdb_record), s);
+  }
+
+private:
+  static inline void
+  deleter(void *r)
+  {
+    basic_kvdb_record * const px =
+      reinterpret_cast<basic_kvdb_record *>(r);
+    const size_t alloc_sz = px->alloc_size + sizeof(*px);
+    px->~basic_kvdb_record();
+    rcu::s_instance.dealloc(px, alloc_sz);
+  }
+
+public:
+  static void
+  release(basic_kvdb_record *r)
+  {
+    if (unlikely(!r))
+      return;
+    rcu::s_instance.free_with_fn(r, deleter);
+  }
+
+  static void
+  release_no_rcu(basic_kvdb_record *r)
+  {
+    if (unlikely(!r))
+      return;
+    deleter(r);
+  }
+
+} PACKED;
+
+template <bool UseConcurrencyControl>
+bool
+kvdb_ordered_index<UseConcurrencyControl>::get(
+    void *txn,
+    const std::string &key,
+    std::string &value, size_t max_bytes_read)
+{
+  typedef basic_kvdb_record<UseConcurrencyControl> kvdb_record;
+  ANON_REGION("kvdb_ordered_index::get:", &private_::kvdb_get_probe0_cg);
+  typename my_btree::value_type v = 0;
+  if (btr.search(varkey(key), v)) {
+    ANON_REGION("kvdb_ordered_index::get:do_read:", &private_::kvdb_get_probe1_cg);
+    const kvdb_record * const r = (const kvdb_record *) v;
+    r->prefetch();
+    r->do_read(value, max_bytes_read);
+    return true;
+  }
+  return false;
+}
+
+template <bool UseConcurrencyControl>
+const char *
+kvdb_ordered_index<UseConcurrencyControl>::put(
+    void *txn,
+    const std::string &key,
+    const std::string &value)
+{
+  typedef basic_kvdb_record<UseConcurrencyControl> kvdb_record;
+  ANON_REGION("kvdb_ordered_index::put:", &private_::kvdb_put_probe0_cg);
+  typename my_btree::value_type v = 0, v_old = 0;
+  if (btr.search(varkey(key), v)) {
+    // easy
+    kvdb_record * const r = (kvdb_record *) v;
+    r->prefetch();
+    lock_guard<kvdb_record> guard(*r);
+    if (r->do_write(value))
+      return 0;
+    // replace
+    kvdb_record * const rnew = kvdb_record::alloc(value);
+    btr.insert(varkey(key), (typename my_btree::value_type) rnew, &v_old, 0);
+    INVARIANT((typename my_btree::value_type) r == v_old);
+    // rcu-free the old record
+    kvdb_record::release(r);
+    return 0;
+  }
+  kvdb_record * const rnew = kvdb_record::alloc(value);
+  if (!btr.insert(varkey(key), (typename my_btree::value_type) rnew, &v_old, 0)) {
+    kvdb_record * const r = (kvdb_record *) v_old;
+    kvdb_record::release(r);
+  }
+  return 0;
+}
+
+template <bool UseConcurrencyControl>
+const char *
+kvdb_ordered_index<UseConcurrencyControl>::insert(void *txn,
+                           const std::string &key,
+                           const std::string &value)
+{
+  typedef basic_kvdb_record<UseConcurrencyControl> kvdb_record;
+  ANON_REGION("kvdb_ordered_index::insert:", &private_::kvdb_insert_probe0_cg);
+  kvdb_record * const rnew = kvdb_record::alloc(value);
+  typename my_btree::value_type v_old = 0;
+  if (!btr.insert(varkey(key), (typename my_btree::value_type) rnew, &v_old, 0)) {
+    kvdb_record * const r = (kvdb_record *) v_old;
+    kvdb_record::release(r);
+  }
+  return 0;
+}
+
+template <typename Btree, bool UseConcurrencyControl>
+class kvdb_wrapper_search_range_callback : public Btree::search_range_callback {
+public:
+  typedef basic_kvdb_record<UseConcurrencyControl> kvdb_record;
+  kvdb_wrapper_search_range_callback(
+      abstract_ordered_index::scan_callback &upcall,
+      str_arena *arena)
+    : upcall(&upcall), arena(arena) {}
+
+  virtual bool
+  invoke(const typename Btree::string_type &k, typename Btree::value_type v)
+  {
+    const kvdb_record * const r = (const kvdb_record *) v;
+    std::string * const s_px = likely(arena) ? arena->next() : nullptr;
+    INVARIANT(s_px && s_px->empty());
+    r->prefetch();
+    r->do_read(*s_px, std::numeric_limits<size_t>::max());
+    return upcall->invoke(k.data(), k.length(), *s_px);
+  }
+
+private:
+  abstract_ordered_index::scan_callback *upcall;
+  str_arena *arena;
+};
+
+template <bool UseConcurrencyControl>
+void
+kvdb_ordered_index<UseConcurrencyControl>::scan(
+    void *txn,
+    const std::string &start_key,
+    const std::string *end_key,
+    scan_callback &callback,
+    str_arena *arena)
+{
+  ANON_REGION("kvdb_ordered_index::scan:", &private_::kvdb_scan_probe0_cg);
+  kvdb_wrapper_search_range_callback<my_btree, UseConcurrencyControl> c(callback, arena);
+  key_type end(end_key ? key_type(*end_key) : key_type());
+  btr.search_range_call(key_type(start_key), end_key ? &end : 0, c, arena->next());
+}
+
+template <bool UseConcurrencyControl>
+void
+kvdb_ordered_index<UseConcurrencyControl>::rscan(
+    void *txn,
+    const std::string &start_key,
+    const std::string *end_key,
+    scan_callback &callback,
+    str_arena *arena)
+{
+  ANON_REGION("kvdb_ordered_index::rscan:", &private_::kvdb_scan_probe0_cg);
+  kvdb_wrapper_search_range_callback<my_btree, UseConcurrencyControl> c(callback, arena);
+  key_type end(end_key ? key_type(*end_key) : key_type());
+  btr.rsearch_range_call(key_type(start_key), end_key ? &end : 0, c, arena->next());
+}
+
+template <bool UseConcurrencyControl>
+void
+kvdb_ordered_index<UseConcurrencyControl>::remove(void *txn, const std::string &key)
+{
+  typedef basic_kvdb_record<UseConcurrencyControl> kvdb_record;
+  ANON_REGION("kvdb_ordered_index::remove:", &private_::kvdb_remove_probe0_cg);
+  typename my_btree::value_type v = 0;
+  if (btr.remove(varkey(key), &v)) {
+    kvdb_record * const r = (kvdb_record *) v;
+    kvdb_record::release(r);
+  }
+}
+
+template <bool UseConcurrencyControl>
+size_t
+kvdb_ordered_index<UseConcurrencyControl>::size() const
+{
+  return btr.size();
+}
+
+template <typename Btree, bool UseConcurrencyControl>
+struct purge_tree_walker : public Btree::tree_walk_callback {
+  typedef basic_kvdb_record<UseConcurrencyControl> kvdb_record;
+
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+  purge_tree_walker()
+    : purge_stats_nodes(0),
+      purge_stats_nosuffix_nodes(0) {}
+  std::map<size_t, size_t> purge_stats_record_size_counts; // just the record
+  std::map<size_t, size_t> purge_stats_alloc_size_counts; // includes overhead
+  std::vector<uint16_t> purge_stats_nkeys_node;
+  size_t purge_stats_nodes;
+  size_t purge_stats_nosuffix_nodes;
+
+  void
+  dump_stats()
+  {
+    size_t v = 0;
+    for (std::vector<uint16_t>::iterator it = purge_stats_nkeys_node.begin();
+        it != purge_stats_nkeys_node.end(); ++it)
+      v += *it;
+    const double avg_nkeys_node = double(v)/double(purge_stats_nkeys_node.size());
+    const double avg_fill_factor = avg_nkeys_node/double(Btree::NKeysPerNode);
+    std::cerr << "btree node stats" << std::endl;
+    std::cerr << "    avg_nkeys_node: " << avg_nkeys_node << std::endl;
+    std::cerr << "    avg_fill_factor: " << avg_fill_factor << std::endl;
+    std::cerr << "    num_nodes: " << purge_stats_nodes << std::endl;
+    std::cerr << "    num_nosuffix_nodes: " << purge_stats_nosuffix_nodes << std::endl;
+    std::cerr << "record size stats (nbytes => count)" << std::endl;
+    for (std::map<size_t, size_t>::iterator it = purge_stats_record_size_counts.begin();
+        it != purge_stats_record_size_counts.end(); ++it)
+      std::cerr << "    " << it->first << " => " << it->second << std::endl;
+    std::cerr << "alloc size stats  (nbytes => count)" << std::endl;
+    for (std::map<size_t, size_t>::iterator it = purge_stats_alloc_size_counts.begin();
+        it != purge_stats_alloc_size_counts.end(); ++it)
+      std::cerr << "    " << (it->first + sizeof(kvdb_record)) << " => " << it->second << std::endl;
+  }
+#endif
+
+  virtual void
+  on_node_begin(const typename Btree::node_opaque_t *n)
+  {
+    INVARIANT(spec_values.empty());
+    spec_values = Btree::ExtractValues(n);
+  }
+
+  virtual void
+  on_node_success()
+  {
+    for (size_t i = 0; i < spec_values.size(); i++) {
+      kvdb_record * const r = (kvdb_record *) spec_values[i].first;
+      purge_stats_record_size_counts[r->size()]++;
+      purge_stats_alloc_size_counts[r->alloc_size]++;
+      kvdb_record::release_no_rcu(r);
+    }
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+    purge_stats_nkeys_node.push_back(spec_values.size());
+    purge_stats_nodes++;
+    for (size_t i = 0; i < spec_values.size(); i++)
+      if (spec_values[i].second)
+        goto done;
+    purge_stats_nosuffix_nodes++;
+done:
+#endif
+    spec_values.clear();
+  }
+
+  virtual void
+  on_node_failure()
+  {
+    spec_values.clear();
+  }
+
+private:
+  std::vector<std::pair<typename Btree::value_type, bool>> spec_values;
+};
+
+template <bool UseConcurrencyControl>
+std::map<std::string, uint64_t>
+kvdb_ordered_index<UseConcurrencyControl>::clear()
+{
+
+  purge_tree_walker<my_btree, UseConcurrencyControl> w;
+  scoped_rcu_region guard;
+  btr.tree_walk(w);
+  btr.clear();
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+  std::cerr << "purging kvdb index: " << name << std::endl;
+  w.dump_stats();
+#endif
+  return std::map<std::string, uint64_t>();
+}
+
+template <bool UseConcurrencyControl>
+abstract_ordered_index *
+kvdb_wrapper<UseConcurrencyControl>::open_index(
+    const std::string &name, size_t value_size_hint, bool mostly_append)
+{
+  return new kvdb_ordered_index<UseConcurrencyControl>(name);
+}
+
+#endif /* _KVDB_WRAPPER_IMPL_H_ */
diff --git a/silo/benchmarks/masstree/README b/silo/benchmarks/masstree/README
new file mode 100644 (file)
index 0000000..b472995
--- /dev/null
@@ -0,0 +1,3 @@
+We strip out pieces of kvtest.hh that we need here- this is bad software
+engineering practice, since we really should make our btree implement the
+Client interface used in the masstree test code.
diff --git a/silo/benchmarks/masstree/kvrandom.cc b/silo/benchmarks/masstree/kvrandom.cc
new file mode 100644 (file)
index 0000000..13e9d69
--- /dev/null
@@ -0,0 +1,38 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012 President and Fellows of Harvard College
+ * Copyright (c) 2012 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file is
+ * legally binding.
+ */
+#include "kvrandom.hh"
+//#include "compiler.hh"
+#include <stdio.h>
+
+const uint32_t kvrandom_psdes_nr::c1[] = {
+    0xBAA96887U, 0x1E17D32CU, 0x03BCDC3CU, 0x0F33D1B2U
+};
+const uint32_t kvrandom_psdes_nr::c2[] = {
+    0x4B0F3B58U, 0xE874F0C3U, 0x6955C5A6U, 0x55A7CA46U
+};
+
+uint32_t kvrandom_psdes_nr::psdes(uint32_t lword, uint32_t irword) {
+    for (int i = 0; i < niter; ++i) {
+       uint32_t iswap = irword;
+       uint32_t ia = irword ^ c1[i];
+       uint32_t il = ia & 0xFFFF, ih = ia >> 16;
+       uint32_t ib = il * il + ~(ih * ih);
+       ia = (ib >> 16) | (ib << 16);
+       irword = lword ^ ((ia ^ c2[i]) + il * ih);
+       lword = iswap;
+    }
+    return irword;
+}
diff --git a/silo/benchmarks/masstree/kvrandom.hh b/silo/benchmarks/masstree/kvrandom.hh
new file mode 100644 (file)
index 0000000..4c6b3be
--- /dev/null
@@ -0,0 +1,100 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012 President and Fellows of Harvard College
+ * Copyright (c) 2012 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file is
+ * legally binding.
+ */
+#ifndef KVRANDOM_HH
+#define KVRANDOM_HH 1
+#include <inttypes.h>
+#include <stdlib.h>
+
+// A simple LCG with parameters from Numerical Recipes.
+class kvrandom_lcg_nr_simple { public:
+    enum { min_value = 0, max_value = 0xFFFFFFFFU };
+    typedef uint32_t value_type;
+    typedef uint32_t seed_type;
+    kvrandom_lcg_nr_simple()
+       : seed_(default_seed) {
+    }
+    explicit kvrandom_lcg_nr_simple(seed_type seed)
+       : seed_(seed) {
+    }
+    void reset(seed_type seed) {
+       seed_ = seed;
+    }
+    value_type next() {
+       return (seed_ = seed_ * a + c);
+    }
+  private:
+    uint32_t seed_;
+    enum { default_seed = 819234718U, a = 1664525U, c = 1013904223U };
+};
+
+// A combination version of the NR LCG that uses only its higher order
+// digits. (In the default NR LCG the lowest bits have less randomness; e.g.,
+// the low bit flips between 0 and 1 with every call.)
+class kvrandom_lcg_nr : public kvrandom_lcg_nr_simple { public:
+    enum { min_value = 0, max_value = 0x7FFFFFFF };
+    typedef int32_t value_type;
+    kvrandom_lcg_nr(seed_type seed) : kvrandom_lcg_nr_simple(seed) {}
+    value_type next() {
+       uint32_t x0 = kvrandom_lcg_nr_simple::next(),
+           x1 = kvrandom_lcg_nr_simple::next();
+       return (x0 >> 15) | ((x1 & 0x7FFE) << 16);
+    }
+};
+
+// A random number generator taken from NR's ran4. Based on hashing.
+class kvrandom_psdes_nr { public:
+    enum { min_value = 0, max_value = 0xFFFFFFFFU };
+    typedef uint32_t value_type;
+    typedef uint32_t seed_type;
+    kvrandom_psdes_nr() {
+       reset(1);
+    }
+    explicit kvrandom_psdes_nr(seed_type seed) {
+       reset(seed);
+    }
+    void reset(seed_type seed) {
+       seed_ = seed;
+       next_ = 1;
+    }
+    value_type next() {
+       uint32_t value = psdes(seed_, next_);
+       ++next_;
+       return value;
+    }
+    value_type operator[](uint32_t index) const {
+       return psdes(seed_, index);
+    }
+  private:
+    uint32_t seed_;
+    uint32_t next_;
+    enum { niter = 4 };
+    static const uint32_t c1[niter], c2[niter];
+    static uint32_t psdes(uint32_t lword, uint32_t irword);
+};
+
+// a wrapper around random(), for backwards compatibility
+class kvrandom_random { public:
+    kvrandom_random() {
+    }
+    void reset(uint32_t seed) {
+       srandom(seed);
+    }
+    int32_t next() const {
+       return random();
+    }
+};
+
+#endif
diff --git a/silo/benchmarks/masstree/kvtest.cc b/silo/benchmarks/masstree/kvtest.cc
new file mode 100644 (file)
index 0000000..f14f688
--- /dev/null
@@ -0,0 +1,179 @@
+#include <iostream>
+#include <vector>
+
+#include <unistd.h>
+
+#include "../../btree_choice.h"
+#include "../../thread.h"
+#include "../../spinbarrier.h"
+#include "../../varkey.h"
+#include "kvrandom.hh"
+
+using namespace std;
+using namespace util;
+
+struct quick_istr {
+private:
+  char buf_[32];
+  char *bbuf_;
+public:
+  inline quick_istr()
+  {
+    set(0);
+  }
+
+  inline quick_istr(unsigned long x, int minlen = 0)
+  {
+    set(x, minlen);
+  }
+
+  inline void
+  set(unsigned long x, int minlen = 0)
+  {
+    bbuf_ = buf_ + sizeof(buf_) - 1;
+    do {
+      *--bbuf_ = (x % 10) + '0';
+      x /= 10;
+    } while (--minlen > 0 || x != 0);
+  }
+
+  inline const char *
+  c_str()
+  {
+    buf_[sizeof(buf_) - 1] = 0;
+    return bbuf_;
+  }
+
+  inline const char *
+  data() const
+  {
+    return bbuf_;
+  }
+
+  inline size_t
+  size() const
+  {
+    return (buf_ + sizeof(buf_) - 1) - bbuf_;
+  }
+
+  inline varkey
+  key() const
+  {
+    return varkey((const uint8_t *) bbuf_, size());
+  }
+};
+
+template <typename T>
+class kvtest_worker : public ndb_thread {
+public:
+  kvtest_worker(btree &btr,
+                spin_barrier &b,
+                unsigned int id,
+                const volatile bool *phases)
+    : ndb_thread(false, string("kvtest-worker")),
+      btr(&btr), b(&b), id(id), phases(phases) {}
+
+  virtual void
+  run()
+  {
+    b->count_down();
+    b->wait_for();
+    T()(*btr, id, phases);
+  }
+
+private:
+  btree *btr;
+  spin_barrier *b;
+  unsigned int id;
+  const volatile bool *phases;
+};
+
+template <typename T>
+class kvtest_runner {
+public:
+  kvtest_runner(unsigned int nthreads)
+    : nthreads(nthreads) {}
+
+  void
+  go()
+  {
+    btree btr;
+    spin_barrier barrier(nthreads);
+    volatile bool phases[T::NPhases] = {0};
+    vector<kvtest_worker<T> *> workers;
+    for (unsigned int i = 0; i < nthreads; i++)
+      workers.push_back(new kvtest_worker<T>(btr, barrier, i, phases));
+    for (unsigned int i = 0; i < nthreads; i++)
+      workers[i]->start();
+    for (unsigned int i = 0; i < T::NPhases; i++) {
+      sleep(T::PhaseRuntimes[i]);
+      phases[i] = 1;
+      __sync_synchronize();
+    }
+    for (unsigned int i = 0; i < nthreads; i++) {
+      workers[i]->join();
+      delete workers[i];
+    }
+  }
+
+public:
+  unsigned int nthreads;
+};
+
+struct kvtest_rw1 {
+  static const size_t NPhases = 2;
+  static const unsigned long PhaseRuntimes[NPhases];
+
+  typedef kvrandom_lcg_nr rand_type;
+
+  void
+  operator()(btree &btr, unsigned int id, const volatile bool *phases) const
+  {
+    const int seed = 31949 + id % 48;
+    rand_type r(seed);
+    timer t;
+    unsigned n;
+    for (n = 0; !phases[0]; ++n) {
+      const int32_t x = (int32_t) r.next();
+      const quick_istr key(x), value(x + 1);
+      btree::value_type v = (btree::value_type) malloc(value.size());
+      NDB_MEMCPY(v, value.data(), value.size());
+      btr.insert(key.key(), v, 0, 0);
+    }
+    const double put_ms = t.lap_ms();
+    int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
+    r.reset(seed);
+    for (unsigned i = 0; i < n; i++)
+      a[i] = (int32_t) r.next();
+    for (unsigned i = 0; i < n; ++i)
+      swap(a[i], a[r.next() % n]);
+
+    t.lap();
+    unsigned g;
+    for (g = 0; g < n && !phases[1]; ++g) {
+      const quick_istr key(a[g]), value(a[g] + 1);
+      btree::value_type v = 0;
+      ALWAYS_ASSERT(btr.search(key.key(), v));
+      ALWAYS_ASSERT(memcmp(value.data(), v, value.size()) == 0);
+    }
+    const double get_ms = t.lap_ms();
+
+    cout << "puts: " << n << ", rate: " << (double(n) / (put_ms / 1000.0)) << " ops/sec/core" << endl;
+    cout << "gets: " << g << ", rate: " << (double(g) / (get_ms / 1000.0)) << " ops/sec/core" << endl;
+
+    // XXX(stephentu): free btree values
+  }
+};
+
+const unsigned long kvtest_rw1::PhaseRuntimes[NPhases] = { 10, 10 }; // seconds
+
+int
+main(int argc, char **argv)
+{
+  ALWAYS_ASSERT(argc == 2);
+  int nthreads = atoi(argv[1]);
+  ALWAYS_ASSERT(nthreads > 0);
+  kvtest_runner<kvtest_rw1> r(nthreads);
+  r.go();
+  return 0;
+}
diff --git a/silo/benchmarks/mysql_wrapper.cc b/silo/benchmarks/mysql_wrapper.cc
new file mode 100644 (file)
index 0000000..f62293c
--- /dev/null
@@ -0,0 +1,255 @@
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <iostream>
+#include <sstream>
+
+#include "../macros.h"
+#include "mysql_wrapper.h"
+
+using namespace std;
+static bool embed_active = false;
+
+static inline void
+print_error_and_bail(MYSQL *conn)
+{
+  cerr << "mysql_error_message: " << mysql_error(conn) << endl;
+  ALWAYS_ASSERT(false);
+}
+
+static inline void
+check_result(MYSQL *conn, int result)
+{
+  if (likely(result == 0))
+    return;
+  print_error_and_bail(conn);
+}
+
+mysql_wrapper::mysql_wrapper(const string &dir, const string &db)
+  : db(db)
+{
+  struct stat st;
+  if (stat(dir.c_str(), &st) != 0) {
+    cerr << "ERROR! The db directory " << dir << " does not exist" << endl;
+    ALWAYS_ASSERT(false);
+  }
+
+  if (!__sync_bool_compare_and_swap(&embed_active, false, true)) {
+    cerr << "only one embedmysql object can exist at once" << endl;
+    ALWAYS_ASSERT(false);
+  }
+
+  char dir_arg[1024];
+  snprintf(dir_arg, sizeof(dir_arg), "--datadir=%s", dir.c_str());
+
+  /**
+       --innodb-buffer-pool-size=$SPACE
+       --innodb_log_file_size=1792M
+       --port=$PORT
+       --transaction_isolation=serializable
+       --max_connections=300
+       --local-infile=1
+       --max_allowed_packet=1073741824
+       --max_heap_table_size=2147483648
+       --group_concat_max_len=1073741824
+       --skip-slave-start
+       --innodb_flush_method=O_DIRECT
+       --log-error
+  */
+
+  const char *mysql_av[] =
+    {
+      "progname",
+      "--skip-grant-tables",
+      dir_arg,
+      "--character-set-server=utf8",
+      "--innodb-buffer-pool-size=4G", // XXX: don't hardocde
+      "--innodb_log_file_size=1792M", // max log file size
+      "--transaction_isolation=serializable",
+      "--innodb_flush_method=O_DIRECT",
+      "--innodb_flush_log_at_trx_commit=0", // only flush log once every second
+      "--sync_binlog=0",
+      "--language=" MYSQL_SHARE_DIR,
+    };
+
+  check_result(0, mysql_library_init(ARRAY_NELEMS(mysql_av), (char **) mysql_av, 0));
+
+  MYSQL *conn = new_connection("");
+  ostringstream b;
+  b << "CREATE DATABASE IF NOT EXISTS " << db << ";";
+  check_result(conn, mysql_query(conn, b.str().c_str()));
+  check_result(conn, mysql_select_db(conn, db.c_str()));
+  mysql_close(conn);
+}
+
+mysql_wrapper::~mysql_wrapper()
+{
+  mysql_server_end();
+  ALWAYS_ASSERT(__sync_bool_compare_and_swap(&embed_active, true, false));
+}
+
+void
+mysql_wrapper::thread_init(bool loader)
+{
+  ALWAYS_ASSERT(tl_conn == NULL);
+  tl_conn = new_connection(db);
+  ALWAYS_ASSERT(tl_conn);
+}
+
+void
+mysql_wrapper::thread_end()
+{
+  ALWAYS_ASSERT(tl_conn);
+  mysql_close(tl_conn);
+  tl_conn = NULL;
+  mysql_thread_end();
+}
+
+void *
+mysql_wrapper::new_txn(
+    uint64_t txn_flags,
+    str_arena &arena,
+    void *buf,
+    TxnProfileHint hint)
+{
+  ALWAYS_ASSERT(tl_conn);
+  check_result(tl_conn, mysql_real_query(tl_conn, "BEGIN", 5));
+  return (void *) tl_conn;
+}
+
+bool
+mysql_wrapper::commit_txn(void *p)
+{
+  ALWAYS_ASSERT(tl_conn == p);
+  return mysql_commit(tl_conn) == 0;
+}
+
+void
+mysql_wrapper::abort_txn(void *p)
+{
+  ALWAYS_ASSERT(tl_conn == p);
+  check_result(tl_conn, mysql_rollback(tl_conn));
+}
+
+abstract_ordered_index *
+mysql_wrapper::open_index(const string &name, size_t value_size_hint, bool mostly_append)
+{
+  ALWAYS_ASSERT(value_size_hint <= 256); // limitation
+  MYSQL *conn = new_connection(db);
+  ostringstream b_create, b_truncate;
+  b_create <<
+    "CREATE TABLE IF NOT EXISTS " << name << " ("
+    "  tbl_key VARBINARY(256) PRIMARY KEY, "
+    "  tbl_value VARBINARY(256) "
+    ") ENGINE=InnoDB;";
+  b_truncate <<
+    "TRUNCATE TABLE " << name << ";";
+  check_result(conn, mysql_query(conn, b_create.str().c_str()));
+  check_result(conn, mysql_query(conn, b_truncate.str().c_str()));
+  check_result(conn, mysql_commit(conn));
+  mysql_close(conn);
+  return new mysql_ordered_index(name);
+}
+
+void
+mysql_wrapper::close_index(abstract_ordered_index *idx)
+{
+  delete idx;
+}
+
+static inline string
+my_escape(MYSQL *conn, const char *p, size_t l)
+{
+  char buf[2*l + 1];
+  unsigned long newl = mysql_real_escape_string(conn, &buf[0], p, l);
+  return string(&buf[0], newl);
+}
+
+bool
+mysql_ordered_index::get(
+    void *txn,
+    const string &key,
+    string &value, size_t max_bytes_read)
+{
+  INVARIANT(txn == mysql_wrapper::tl_conn);
+  ALWAYS_ASSERT(key.size() <= 256);
+  ostringstream b;
+  b << "SELECT tbl_value FROM " << name << " WHERE tbl_key = '" << my_escape(mysql_wrapper::tl_conn, key.data(), key.size()) << "';";
+  string q = b.str();
+  check_result(mysql_wrapper::tl_conn, mysql_real_query(mysql_wrapper::tl_conn, q.data(), q.size()));
+  MYSQL_RES *res = mysql_store_result(mysql_wrapper::tl_conn);
+  ALWAYS_ASSERT(res);
+  MYSQL_ROW row = mysql_fetch_row(res);
+  bool ret = false;
+  if (row) {
+    unsigned long *lengths = mysql_fetch_lengths(res);
+    value.assign(row[0], min(lengths[0], max_bytes_read));
+    ret = true;
+  }
+  mysql_free_result(res);
+  return ret;
+}
+
+const char *
+mysql_ordered_index::put(
+    void *txn,
+    const string &key,
+    const string &value)
+{
+  INVARIANT(txn == mysql_wrapper::tl_conn);
+  ALWAYS_ASSERT(key.size() <= 256);
+  ALWAYS_ASSERT(value.size() <= 256);
+  string escaped_key = my_escape(mysql_wrapper::tl_conn, key.data(), key.size());
+  string escaped_value = my_escape(mysql_wrapper::tl_conn, value.data(), value.size());
+  ostringstream b;
+  b << "UPDATE " << name << " SET tbl_value='" << escaped_value << "' WHERE tbl_key='" << escaped_key << "';";
+  string q = b.str();
+  check_result(mysql_wrapper::tl_conn, mysql_real_query(mysql_wrapper::tl_conn, q.data(), q.size()));
+  my_ulonglong ret = mysql_affected_rows(mysql_wrapper::tl_conn);
+  if (unlikely(ret == (my_ulonglong) -1))
+    print_error_and_bail(mysql_wrapper::tl_conn);
+  if (ret)
+    return 0;
+  ostringstream b1;
+  b1 << "INSERT INTO " << name << " VALUES ('" << escaped_key << "', '" << escaped_value << "');";
+  string q1 = b1.str();
+  check_result(mysql_wrapper::tl_conn, mysql_real_query(mysql_wrapper::tl_conn, q1.data(), q1.size()));
+  return 0;
+}
+
+const char *
+mysql_ordered_index::insert(
+    void *txn,
+    const string &key,
+    const string &value)
+{
+  INVARIANT(txn == mysql_wrapper::tl_conn);
+  ALWAYS_ASSERT(key.size() <= 256);
+  ALWAYS_ASSERT(value.size() <= 256);
+  string escaped_key = my_escape(mysql_wrapper::tl_conn, key.data(), key.size());
+  string escaped_value = my_escape(mysql_wrapper::tl_conn, value.data(), value.size());
+  ostringstream b1;
+  b1 << "INSERT INTO " << name << " VALUES ('" << escaped_key << "', '" << escaped_value << "');";
+  string q1 = b1.str();
+  check_result(mysql_wrapper::tl_conn, mysql_real_query(mysql_wrapper::tl_conn, q1.data(), q1.size()));
+  return 0;
+}
+
+MYSQL *
+mysql_wrapper::new_connection(const string &db)
+{
+  MYSQL *conn = mysql_init(0);
+  mysql_options(conn, MYSQL_OPT_USE_EMBEDDED_CONNECTION, 0);
+  if (!mysql_real_connect(conn, 0, 0, 0, db.c_str(), 0, 0, CLIENT_FOUND_ROWS | CLIENT_MULTI_STATEMENTS)) {
+    mysql_close(conn);
+    cerr << "mysql_real_connect: " << mysql_error(conn) << endl;
+    ALWAYS_ASSERT(false);
+  }
+  check_result(conn, mysql_autocommit(conn, 0));
+  return conn;
+}
+
+__thread MYSQL *mysql_wrapper::tl_conn = NULL;
diff --git a/silo/benchmarks/mysql_wrapper.h b/silo/benchmarks/mysql_wrapper.h
new file mode 100644 (file)
index 0000000..f0f0014
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef _MYSQL_WRAPPER_H_
+#define _MYSQL_WRAPPER_H_
+
+#include <string>
+#include <mysql/mysql.h>
+
+#include "abstract_db.h"
+#include "../macros.h"
+
+class mysql_wrapper : public abstract_db {
+  friend class mysql_ordered_index;
+public:
+  mysql_wrapper(const std::string &dir, const std::string &db);
+  ~mysql_wrapper();
+
+  virtual void thread_init(bool loader);
+  virtual void thread_end();
+
+  virtual void *new_txn(
+      uint64_t txn_flags,
+      str_arena &arena,
+      void *buf,
+      TxnProfileHint hint);
+  virtual bool commit_txn(void *txn);
+  virtual void abort_txn(void *txn);
+
+  virtual abstract_ordered_index *
+  open_index(const std::string &name,
+             size_t value_size_hint,
+             bool mostly_append);
+
+  virtual void
+  close_index(abstract_ordered_index *idx);
+
+private:
+  std::string db;
+  MYSQL *new_connection(const std::string &db);
+  static __thread MYSQL *tl_conn;
+};
+
+class mysql_ordered_index : public abstract_ordered_index {
+public:
+  mysql_ordered_index(const std::string &name) : name(name) {}
+
+  virtual bool get(
+      void *txn,
+      const std::string &key,
+      std::string &value,
+      size_t max_bytes_read);
+
+  virtual const char * put(
+      void *txn,
+      const std::string &key,
+      const std::string &value);
+
+  virtual const char * insert(
+      void *txn,
+      const std::string &key,
+      const std::string &value);
+
+  virtual void scan(
+      void *txn,
+      const std::string &key,
+      const std::string *value,
+      scan_callback &callback,
+      str_arena *arena)
+  {
+    NDB_UNIMPLEMENTED("scan");
+  }
+
+  virtual void rscan(
+      void *txn,
+      const std::string &start_key,
+      const std::string *end_key,
+      scan_callback &callback,
+      str_arena *arena)
+  {
+    NDB_UNIMPLEMENTED("rscan");
+  }
+
+  virtual size_t
+  size() const
+  {
+    NDB_UNIMPLEMENTED("size");
+  }
+
+  virtual std::map<std::string, uint64_t>
+  clear()
+  {
+    NDB_UNIMPLEMENTED("clear");
+  }
+
+private:
+  std::string name;
+};
+
+#endif /* _MYSQL_WRAPPER_H_ */
diff --git a/silo/benchmarks/ndb_wrapper.h b/silo/benchmarks/ndb_wrapper.h
new file mode 100644 (file)
index 0000000..e659f23
--- /dev/null
@@ -0,0 +1,164 @@
+#ifndef _NDB_WRAPPER_H_
+#define _NDB_WRAPPER_H_
+
+#include "abstract_db.h"
+#include "../txn_btree.h"
+
+namespace private_ {
+  struct ndbtxn {
+    abstract_db::TxnProfileHint hint;
+    char buf[0];
+  } PACKED;
+
+  // XXX: doesn't check to make sure you are passing in an ndbtx
+  // of the right hint
+  template <template <typename> class Transaction, typename Traits>
+  struct cast_base {
+    typedef Transaction<Traits> type;
+    inline ALWAYS_INLINE type *
+    operator()(struct ndbtxn *p) const
+    {
+      return reinterpret_cast<type *>(&p->buf[0]);
+    }
+  };
+
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, ndb_get_probe0, ndb_get_probe0_cg)
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, ndb_put_probe0, ndb_put_probe0_cg)
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, ndb_insert_probe0, ndb_insert_probe0_cg)
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, ndb_scan_probe0, ndb_scan_probe0_cg)
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, ndb_remove_probe0, ndb_remove_probe0_cg)
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, ndb_dtor_probe0, ndb_dtor_probe0_cg)
+}
+
+template <template <typename> class Transaction>
+class ndb_wrapper : public abstract_db {
+protected:
+  typedef private_::ndbtxn ndbtxn;
+  template <typename Traits>
+    using cast = private_::cast_base<Transaction, Traits>;
+
+public:
+
+  ndb_wrapper(
+      const std::vector<std::string> &logfiles,
+      const std::vector<std::vector<unsigned>> &assignments_given,
+      bool call_fsync,
+      bool use_compression,
+      bool fake_writes);
+
+  virtual ssize_t txn_max_batch_size() const OVERRIDE { return 100; }
+
+  virtual void
+  do_txn_epoch_sync() const
+  {
+    txn_epoch_sync<Transaction>::sync();
+  }
+
+  virtual void
+  do_txn_finish() const
+  {
+    txn_epoch_sync<Transaction>::finish();
+  }
+
+  virtual void
+  thread_init(bool loader)
+  {
+    txn_epoch_sync<Transaction>::thread_init(loader);
+  }
+
+  virtual void
+  thread_end()
+  {
+    txn_epoch_sync<Transaction>::thread_end();
+  }
+
+  virtual std::tuple<uint64_t, uint64_t, double>
+  get_ntxn_persisted() const
+  {
+    return txn_epoch_sync<Transaction>::compute_ntxn_persisted();
+  }
+
+  virtual void
+  reset_ntxn_persisted()
+  {
+    txn_epoch_sync<Transaction>::reset_ntxn_persisted();
+  }
+
+  virtual size_t
+  sizeof_txn_object(uint64_t txn_flags) const;
+
+  virtual void *new_txn(
+      uint64_t txn_flags,
+      str_arena &arena,
+      void *buf,
+      TxnProfileHint hint);
+  virtual bool commit_txn(void *txn);
+  virtual void abort_txn(void *txn);
+  virtual void print_txn_debug(void *txn) const;
+  virtual std::map<std::string, uint64_t> get_txn_counters(void *txn) const;
+
+  virtual abstract_ordered_index *
+  open_index(const std::string &name,
+             size_t value_size_hint,
+             bool mostly_append);
+
+  virtual void
+  close_index(abstract_ordered_index *idx);
+
+};
+
+template <template <typename> class Transaction>
+class ndb_ordered_index : public abstract_ordered_index {
+protected:
+  typedef private_::ndbtxn ndbtxn;
+  template <typename Traits>
+    using cast = private_::cast_base<Transaction, Traits>;
+
+public:
+  ndb_ordered_index(const std::string &name, size_t value_size_hint, bool mostly_append);
+  virtual bool get(
+      void *txn,
+      const std::string &key,
+      std::string &value, size_t max_bytes_read);
+  virtual const char * put(
+      void *txn,
+      const std::string &key,
+      const std::string &value);
+  virtual const char * put(
+      void *txn,
+      std::string &&key,
+      std::string &&value);
+  virtual const char *
+  insert(void *txn,
+         const std::string &key,
+         const std::string &value);
+  virtual const char *
+  insert(void *txn,
+         std::string &&key,
+         std::string &&value);
+  virtual void scan(
+      void *txn,
+      const std::string &start_key,
+      const std::string *end_key,
+      scan_callback &callback,
+      str_arena *arena);
+  virtual void rscan(
+      void *txn,
+      const std::string &start_key,
+      const std::string *end_key,
+      scan_callback &callback,
+      str_arena *arena);
+  virtual void remove(
+      void *txn,
+      const std::string &key);
+  virtual void remove(
+      void *txn,
+      std::string &&key);
+  virtual size_t size() const;
+  virtual std::map<std::string, uint64_t> clear();
+private:
+  std::string name;
+  txn_btree<Transaction> btr;
+};
+
+#endif /* _NDB_WRAPPER_H_ */
diff --git a/silo/benchmarks/ndb_wrapper_impl.h b/silo/benchmarks/ndb_wrapper_impl.h
new file mode 100644 (file)
index 0000000..412d68f
--- /dev/null
@@ -0,0 +1,600 @@
+#ifndef _NDB_WRAPPER_IMPL_H_
+#define _NDB_WRAPPER_IMPL_H_
+
+#include <stdint.h>
+#include "ndb_wrapper.h"
+#include "../counter.h"
+#include "../rcu.h"
+#include "../varkey.h"
+#include "../macros.h"
+#include "../util.h"
+#include "../scopedperf.hh"
+#include "../txn.h"
+//#include "../txn_proto1_impl.h"
+#include "../txn_proto2_impl.h"
+#include "../tuple.h"
+
+struct hint_default_traits : public default_transaction_traits {
+  typedef str_arena StringAllocator;
+};
+
+// ycsb profiles
+
+struct hint_kv_get_put_traits {
+  static const size_t read_set_expected_size = 1;
+  static const size_t write_set_expected_size = 1;
+  static const size_t absent_set_expected_size = 1;
+  static const bool stable_input_memory = true;
+  static const bool hard_expected_sizes = true;
+  static const bool read_own_writes = false;
+  typedef str_arena StringAllocator;
+};
+
+struct hint_kv_rmw_traits : public hint_kv_get_put_traits {};
+
+struct hint_kv_scan_traits {
+  static const size_t read_set_expected_size = 100;
+  static const size_t write_set_expected_size = 1;
+  static const size_t absent_set_expected_size = read_set_expected_size / 7 + 1;
+  static const bool stable_input_memory = true;
+  static const bool hard_expected_sizes = false;
+  static const bool read_own_writes = false;
+  typedef str_arena StringAllocator;
+};
+
+// tpcc profiles
+
+struct hint_read_only_traits {
+  static const size_t read_set_expected_size = 1;
+  static const size_t write_set_expected_size = 1;
+  static const size_t absent_set_expected_size = 1;
+  static const bool stable_input_memory = true;
+  static const bool hard_expected_sizes = true;
+  static const bool read_own_writes = false;
+  typedef str_arena StringAllocator;
+};
+
+struct hint_tpcc_new_order_traits {
+  static const size_t read_set_expected_size = 35;
+  static const size_t write_set_expected_size = 35;
+  static const size_t absent_set_expected_size = 1;
+  static const bool stable_input_memory = true;
+  static const bool hard_expected_sizes = true;
+  static const bool read_own_writes = false;
+  typedef str_arena StringAllocator;
+};
+
+struct hint_tpcc_payment_traits {
+  static const size_t read_set_expected_size = 85;
+  static const size_t write_set_expected_size = 10;
+  static const size_t absent_set_expected_size = 15;
+  static const bool stable_input_memory = true;
+  static const bool hard_expected_sizes = false;
+  static const bool read_own_writes = false;
+  typedef str_arena StringAllocator;
+};
+
+struct hint_tpcc_delivery_traits {
+  static const size_t read_set_expected_size = 175;
+  static const size_t write_set_expected_size = 175;
+  static const size_t absent_set_expected_size = 35;
+  static const bool stable_input_memory = true;
+  static const bool hard_expected_sizes = false;
+  static const bool read_own_writes = false;
+  typedef str_arena StringAllocator;
+};
+
+struct hint_tpcc_order_status_traits {
+  static const size_t read_set_expected_size = 95;
+  static const size_t write_set_expected_size = 1;
+  static const size_t absent_set_expected_size = 25;
+  static const bool stable_input_memory = true;
+  static const bool hard_expected_sizes = false;
+  static const bool read_own_writes = false;
+  typedef str_arena StringAllocator;
+};
+
+struct hint_tpcc_order_status_read_only_traits : public hint_read_only_traits {};
+
+struct hint_tpcc_stock_level_traits {
+  static const size_t read_set_expected_size = 500;
+  static const size_t write_set_expected_size = 1;
+  static const size_t absent_set_expected_size = 25;
+  static const bool stable_input_memory = true;
+  static const bool hard_expected_sizes = false;
+  static const bool read_own_writes = false;
+  typedef str_arena StringAllocator;
+};
+
+struct hint_tpcc_stock_level_read_only_traits : public hint_read_only_traits {};
+
+#define TXN_PROFILE_HINT_OP(x) \
+  x(abstract_db::HINT_DEFAULT, hint_default_traits) \
+  x(abstract_db::HINT_KV_GET_PUT, hint_kv_get_put_traits) \
+  x(abstract_db::HINT_KV_RMW, hint_kv_rmw_traits) \
+  x(abstract_db::HINT_KV_SCAN, hint_kv_scan_traits) \
+  x(abstract_db::HINT_TPCC_NEW_ORDER, hint_tpcc_new_order_traits) \
+  x(abstract_db::HINT_TPCC_PAYMENT, hint_tpcc_payment_traits) \
+  x(abstract_db::HINT_TPCC_DELIVERY, hint_tpcc_delivery_traits) \
+  x(abstract_db::HINT_TPCC_ORDER_STATUS, hint_tpcc_order_status_traits) \
+  x(abstract_db::HINT_TPCC_ORDER_STATUS_READ_ONLY, hint_tpcc_order_status_read_only_traits) \
+  x(abstract_db::HINT_TPCC_STOCK_LEVEL, hint_tpcc_stock_level_traits) \
+  x(abstract_db::HINT_TPCC_STOCK_LEVEL_READ_ONLY, hint_tpcc_stock_level_read_only_traits)
+
+template <template <typename> class Transaction>
+ndb_wrapper<Transaction>::ndb_wrapper(
+    const std::vector<std::string> &logfiles,
+    const std::vector<std::vector<unsigned>> &assignments_given,
+    bool call_fsync,
+    bool use_compression,
+    bool fake_writes)
+{
+  if (logfiles.empty())
+    return;
+  std::vector<std::vector<unsigned>> assignments_used;
+  txn_logger::Init(
+      nthreads, logfiles, assignments_given, &assignments_used,
+      call_fsync,
+      use_compression,
+      fake_writes);
+  if (verbose) {
+    std::cerr << "[logging subsystem]" << std::endl;
+    std::cerr << "  assignments: " << assignments_used << std::endl;
+    std::cerr << "  call fsync : " << call_fsync       << std::endl;
+    std::cerr << "  compression: " << use_compression  << std::endl;
+    std::cerr << "  fake_writes: " << fake_writes      << std::endl;
+  }
+}
+
+template <template <typename> class Transaction>
+size_t
+ndb_wrapper<Transaction>::sizeof_txn_object(uint64_t txn_flags) const
+{
+#define MY_OP_X(a, b) sizeof(typename cast< b >::type),
+  const size_t xs[] = {
+    TXN_PROFILE_HINT_OP(MY_OP_X)
+  };
+#undef MY_OP_X
+  size_t xmax = 0;
+  for (size_t i = 0; i < ARRAY_NELEMS(xs); i++)
+    xmax = std::max(xmax, xs[i]);
+  return xmax;
+}
+
+template <template <typename> class Transaction>
+void *
+ndb_wrapper<Transaction>::new_txn(
+    uint64_t txn_flags,
+    str_arena &arena,
+    void *buf,
+    TxnProfileHint hint)
+{
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(buf);
+  p->hint = hint;
+#define MY_OP_X(a, b) \
+  case a: \
+    new (&p->buf[0]) typename cast< b >::type(txn_flags, arena); \
+    return p;
+  switch (hint) {
+    TXN_PROFILE_HINT_OP(MY_OP_X)
+  default:
+    ALWAYS_ASSERT(false);
+  }
+#undef MY_OP_X
+  return 0;
+}
+
+template <typename T>
+static inline ALWAYS_INLINE void
+Destroy(T *t)
+{
+  PERF_DECL(static std::string probe1_name(std::string(__PRETTY_FUNCTION__) + std::string(":total:")));
+  ANON_REGION(probe1_name.c_str(), &private_::ndb_dtor_probe0_cg);
+  t->~T();
+}
+
+template <template <typename> class Transaction>
+bool
+ndb_wrapper<Transaction>::commit_txn(void *txn)
+{
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      const bool ret = t->commit(); \
+      Destroy(t); \
+      return ret; \
+    }
+  switch (p->hint) {
+    TXN_PROFILE_HINT_OP(MY_OP_X)
+  default:
+    ALWAYS_ASSERT(false);
+  }
+#undef MY_OP_X
+  return false;
+}
+
+template <template <typename> class Transaction>
+void
+ndb_wrapper<Transaction>::abort_txn(void *txn)
+{
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      t->abort(); \
+      Destroy(t); \
+      return; \
+    }
+  switch (p->hint) {
+    TXN_PROFILE_HINT_OP(MY_OP_X)
+  default:
+    ALWAYS_ASSERT(false);
+  }
+#undef MY_OP_X
+}
+
+template <template <typename> class Transaction>
+void
+ndb_wrapper<Transaction>::print_txn_debug(void *txn) const
+{
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      t->dump_debug_info(); \
+      return; \
+    }
+  switch (p->hint) {
+    TXN_PROFILE_HINT_OP(MY_OP_X)
+  default:
+    ALWAYS_ASSERT(false);
+  }
+#undef MY_OP_X
+}
+
+template <template <typename> class Transaction>
+std::map<std::string, uint64_t>
+ndb_wrapper<Transaction>::get_txn_counters(void *txn) const
+{
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      return t->get_txn_counters(); \
+    }
+  switch (p->hint) {
+    TXN_PROFILE_HINT_OP(MY_OP_X)
+  default:
+    ALWAYS_ASSERT(false);
+  }
+#undef MY_OP_X
+  return std::map<std::string, uint64_t>();
+}
+
+template <template <typename> class Transaction>
+abstract_ordered_index *
+ndb_wrapper<Transaction>::open_index(const std::string &name, size_t value_size_hint, bool mostly_append)
+{
+  return new ndb_ordered_index<Transaction>(name, value_size_hint, mostly_append);
+}
+
+template <template <typename> class Transaction>
+void
+ndb_wrapper<Transaction>::close_index(abstract_ordered_index *idx)
+{
+  delete idx;
+}
+
+template <template <typename> class Transaction>
+ndb_ordered_index<Transaction>::ndb_ordered_index(
+    const std::string &name, size_t value_size_hint, bool mostly_append)
+  : name(name), btr(value_size_hint, mostly_append, name)
+{
+  // for debugging
+  //std::cerr << name << " : btree= "
+  //          << btr.get_underlying_btree()
+  //          << std::endl;
+}
+
+template <template <typename> class Transaction>
+bool
+ndb_ordered_index<Transaction>::get(
+    void *txn,
+    const std::string &key,
+    std::string &value, size_t max_bytes_read)
+{
+  PERF_DECL(static std::string probe1_name(std::string(__PRETTY_FUNCTION__) + std::string(":total:")));
+  ANON_REGION(probe1_name.c_str(), &private_::ndb_get_probe0_cg);
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+  try {
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      if (!btr.search(*t, key, value, max_bytes_read)) \
+        return false; \
+      return true; \
+    }
+    switch (p->hint) {
+      TXN_PROFILE_HINT_OP(MY_OP_X)
+    default:
+      ALWAYS_ASSERT(false);
+    }
+#undef MY_OP_X
+    INVARIANT(!value.empty());
+    return true;
+  } catch (transaction_abort_exception &ex) {
+    throw abstract_db::abstract_abort_exception();
+  }
+}
+
+// XXX: find way to remove code duplication below using C++ templates!
+
+template <template <typename> class Transaction>
+const char *
+ndb_ordered_index<Transaction>::put(
+    void *txn,
+    const std::string &key,
+    const std::string &value)
+{
+  PERF_DECL(static std::string probe1_name(std::string(__PRETTY_FUNCTION__) + std::string(":total:")));
+  ANON_REGION(probe1_name.c_str(), &private_::ndb_put_probe0_cg);
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+  try {
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      btr.put(*t, key, value); \
+      return 0; \
+    }
+    switch (p->hint) {
+      TXN_PROFILE_HINT_OP(MY_OP_X)
+    default:
+      ALWAYS_ASSERT(false);
+    }
+#undef MY_OP_X
+  } catch (transaction_abort_exception &ex) {
+    throw abstract_db::abstract_abort_exception();
+  }
+  return 0;
+}
+
+template <template <typename> class Transaction>
+const char *
+ndb_ordered_index<Transaction>::put(
+    void *txn,
+    std::string &&key,
+    std::string &&value)
+{
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+  try {
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      btr.put(*t, std::move(key), std::move(value)); \
+      return 0; \
+    }
+    switch (p->hint) {
+      TXN_PROFILE_HINT_OP(MY_OP_X)
+    default:
+      ALWAYS_ASSERT(false);
+    }
+#undef MY_OP_X
+  } catch (transaction_abort_exception &ex) {
+    throw abstract_db::abstract_abort_exception();
+  }
+  return 0;
+}
+
+template <template <typename> class Transaction>
+const char *
+ndb_ordered_index<Transaction>::insert(
+    void *txn,
+    const std::string &key,
+    const std::string &value)
+{
+  PERF_DECL(static std::string probe1_name(std::string(__PRETTY_FUNCTION__) + std::string(":total:")));
+  ANON_REGION(probe1_name.c_str(), &private_::ndb_insert_probe0_cg);
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+  try {
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      btr.insert(*t, key, value); \
+      return 0; \
+    }
+    switch (p->hint) {
+      TXN_PROFILE_HINT_OP(MY_OP_X)
+    default:
+      ALWAYS_ASSERT(false);
+    }
+#undef MY_OP_X
+  } catch (transaction_abort_exception &ex) {
+    throw abstract_db::abstract_abort_exception();
+  }
+  return 0;
+}
+
+template <template <typename> class Transaction>
+const char *
+ndb_ordered_index<Transaction>::insert(
+    void *txn,
+    std::string &&key,
+    std::string &&value)
+{
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+  try {
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      btr.insert(*t, std::move(key), std::move(value)); \
+      return 0; \
+    }
+    switch (p->hint) {
+      TXN_PROFILE_HINT_OP(MY_OP_X)
+    default:
+      ALWAYS_ASSERT(false);
+    }
+#undef MY_OP_X
+  } catch (transaction_abort_exception &ex) {
+    throw abstract_db::abstract_abort_exception();
+  }
+  return 0;
+}
+
+template <template <typename> class Transaction>
+class ndb_wrapper_search_range_callback : public txn_btree<Transaction>::search_range_callback {
+public:
+  ndb_wrapper_search_range_callback(abstract_ordered_index::scan_callback &upcall)
+    : upcall(&upcall) {}
+
+  virtual bool
+  invoke(const typename txn_btree<Transaction>::keystring_type &k,
+         const typename txn_btree<Transaction>::string_type &v)
+  {
+    return upcall->invoke(k.data(), k.length(), v);
+  }
+
+private:
+  abstract_ordered_index::scan_callback *upcall;
+};
+
+template <template <typename> class Transaction>
+void
+ndb_ordered_index<Transaction>::scan(
+    void *txn,
+    const std::string &start_key,
+    const std::string *end_key,
+    scan_callback &callback,
+    str_arena *arena)
+{
+  PERF_DECL(static std::string probe1_name(std::string(__PRETTY_FUNCTION__) + std::string(":total:")));
+  ANON_REGION(probe1_name.c_str(), &private_::ndb_scan_probe0_cg);
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+  ndb_wrapper_search_range_callback<Transaction> c(callback);
+  try {
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      btr.search_range_call(*t, start_key, end_key, c); \
+      return; \
+    }
+    switch (p->hint) {
+      TXN_PROFILE_HINT_OP(MY_OP_X)
+    default:
+      ALWAYS_ASSERT(false);
+    }
+#undef MY_OP_X
+  } catch (transaction_abort_exception &ex) {
+    throw abstract_db::abstract_abort_exception();
+  }
+}
+
+template <template <typename> class Transaction>
+void
+ndb_ordered_index<Transaction>::rscan(
+    void *txn,
+    const std::string &start_key,
+    const std::string *end_key,
+    scan_callback &callback,
+    str_arena *arena)
+{
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+  ndb_wrapper_search_range_callback<Transaction> c(callback);
+  try {
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      btr.rsearch_range_call(*t, start_key, end_key, c); \
+      return; \
+    }
+    switch (p->hint) {
+      TXN_PROFILE_HINT_OP(MY_OP_X)
+    default:
+      ALWAYS_ASSERT(false);
+    }
+#undef MY_OP_X
+  } catch (transaction_abort_exception &ex) {
+    throw abstract_db::abstract_abort_exception();
+  }
+}
+
+template <template <typename> class Transaction>
+void
+ndb_ordered_index<Transaction>::remove(void *txn, const std::string &key)
+{
+  PERF_DECL(static std::string probe1_name(std::string(__PRETTY_FUNCTION__) + std::string(":total:")));
+  ANON_REGION(probe1_name.c_str(), &private_::ndb_remove_probe0_cg);
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+  try {
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      btr.remove(*t, key); \
+      return; \
+    }
+    switch (p->hint) {
+      TXN_PROFILE_HINT_OP(MY_OP_X)
+    default:
+      ALWAYS_ASSERT(false);
+    }
+#undef MY_OP_X
+  } catch (transaction_abort_exception &ex) {
+    throw abstract_db::abstract_abort_exception();
+  }
+}
+
+template <template <typename> class Transaction>
+void
+ndb_ordered_index<Transaction>::remove(void *txn, std::string &&key)
+{
+  ndbtxn * const p = reinterpret_cast<ndbtxn *>(txn);
+  try {
+#define MY_OP_X(a, b) \
+  case a: \
+    { \
+      auto t = cast< b >()(p); \
+      btr.remove(*t, std::move(key)); \
+      return; \
+    }
+    switch (p->hint) {
+      TXN_PROFILE_HINT_OP(MY_OP_X)
+    default:
+      ALWAYS_ASSERT(false);
+    }
+#undef MY_OP_X
+  } catch (transaction_abort_exception &ex) {
+    throw abstract_db::abstract_abort_exception();
+  }
+}
+
+template <template <typename> class Transaction>
+size_t
+ndb_ordered_index<Transaction>::size() const
+{
+  return btr.size_estimate();
+}
+
+template <template <typename> class Transaction>
+std::map<std::string, uint64_t>
+ndb_ordered_index<Transaction>::clear()
+{
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+  std::cerr << "purging txn index: " << name << std::endl;
+#endif
+  return btr.unsafe_purge(true);
+}
+
+#endif /* _NDB_WRAPPER_IMPL_H_ */
diff --git a/silo/benchmarks/plotter.py b/silo/benchmarks/plotter.py
new file mode 100644 (file)
index 0000000..cf35280
--- /dev/null
@@ -0,0 +1,16 @@
+import matplotlib
+matplotlib.use('Agg')
+import pylab as plt
+
+import sys
+
+if __name__ == '__main__':
+  (_, fname, oname) = sys.argv
+  execfile(fname)
+  for name, data in zip(DBS, RESULTS):
+    plt.plot(THREADS, data)
+  plt.xlabel('num threads')
+  plt.ylabel('ops/sec')
+  plt.title('')
+  plt.legend(DBS, loc='lower right')
+  plt.savefig(oname)
diff --git a/silo/benchmarks/queue.cc b/silo/benchmarks/queue.cc
new file mode 100644 (file)
index 0000000..cb29501
--- /dev/null
@@ -0,0 +1,296 @@
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <utility>
+#include <string>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../macros.h"
+#include "../varkey.h"
+#include "../thread.h"
+#include "../util.h"
+#include "../spinbarrier.h"
+
+#include "bench.h"
+
+using namespace std;
+using namespace util;
+
+static size_t nkeys;
+
+static inline string
+queue_key(uint64_t id0, uint64_t id1)
+{
+  big_endian_trfm<uint64_t> t;
+  string buf(2 * sizeof(uint64_t), 0);
+  uint64_t *p = (uint64_t *) &buf[0];
+  *p++ = t(id0);
+  *p++ = t(id1);
+  return buf;
+}
+
+static const string queue_values("ABCDEFGH");
+
+class queue_worker : public bench_worker {
+public:
+  queue_worker(unsigned int worker_id,
+               unsigned long seed, abstract_db *db,
+               const map<string, abstract_ordered_index *> &open_tables,
+               spin_barrier *barrier_a, spin_barrier *barrier_b,
+               uint64_t id, bool consumer)
+    : bench_worker(worker_id, false, seed, db,
+                   open_tables, barrier_a, barrier_b),
+      tbl(open_tables.at("table")), id(id), consumer(consumer),
+      ctr(consumer ? 0 : nkeys)
+  {
+  }
+
+  txn_result
+  txn_produce()
+  {
+    void *txn = db->new_txn(txn_flags, arena, txn_buf());
+    try {
+      const string k = queue_key(id, ctr);
+      tbl->insert(txn, k, queue_values);
+      if (likely(db->commit_txn(txn))) {
+        ctr++;
+        return txn_result(true, queue_values.size());
+      }
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnProduce(bench_worker *w)
+  {
+    return static_cast<queue_worker *>(w)->txn_produce();
+  }
+
+  txn_result
+  txn_consume()
+  {
+    void *txn = db->new_txn(txn_flags, arena, txn_buf());
+    try {
+      const string lowk = queue_key(id, 0);
+      const string highk = queue_key(id, numeric_limits<uint64_t>::max());
+      limit_callback c(1);
+      tbl->scan(txn, lowk, &highk, c);
+      ssize_t ret = 0;
+      if (likely(!c.values.empty())) {
+        ALWAYS_ASSERT(c.values.size() == 1);
+        const string &k = c.values.front().first;
+        tbl->remove(txn, k);
+        ret = -queue_values.size();
+      }
+      if (likely(db->commit_txn(txn)))
+        return txn_result(true, ret);
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnConsume(bench_worker *w)
+  {
+    return static_cast<queue_worker *>(w)->txn_consume();
+  }
+
+  txn_result
+  txn_consume_scanhint()
+  {
+    void *txn = db->new_txn(txn_flags, arena, txn_buf());
+    try {
+      const string lowk = queue_key(id, ctr);
+      const string highk = queue_key(id, numeric_limits<uint64_t>::max());
+      limit_callback c(1);
+      tbl->scan(txn, lowk, &highk, c);
+      const bool found = !c.values.empty();
+      ssize_t ret = 0;
+      if (likely(found)) {
+        ALWAYS_ASSERT(c.values.size() == 1);
+        const string &k = c.values.front().first;
+        tbl->remove(txn, k);
+        ret = -queue_values.size();
+      }
+      if (likely(db->commit_txn(txn))) {
+        if (likely(found)) ctr++;
+        return txn_result(true, ret);
+      }
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnConsumeScanHint(bench_worker *w)
+  {
+    return static_cast<queue_worker *>(w)->txn_consume_scanhint();
+  }
+
+  txn_result
+  txn_consume_noscan()
+  {
+    void *txn = db->new_txn(txn_flags, arena, txn_buf());
+    try {
+      const string k = queue_key(id, ctr);
+      string v;
+      bool found = false;
+      ssize_t ret = 0;
+      if (likely((found = tbl->get(txn, k, v)))) {
+        tbl->remove(txn, k);
+        ret = -queue_values.size();
+      }
+      if (likely(db->commit_txn(txn))) {
+        if (likely(found)) ctr++;
+        return txn_result(true, ret);
+      }
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnConsumeNoScan(bench_worker *w)
+  {
+    return static_cast<queue_worker *>(w)->txn_consume_noscan();
+  }
+
+  virtual workload_desc_vec
+  get_workload() const
+  {
+    workload_desc_vec w;
+    if (consumer)
+      w.push_back(workload_desc("Consume", 1.0, TxnConsume));
+      //w.push_back(workload_desc("ConsumeScanHint", 1.0, TxnConsumeScanHint));
+      //w.push_back(workload_desc("ConsumeNoScan", 1.0, TxnConsumeNoScan));
+    else
+      w.push_back(workload_desc("Produce", 1.0, TxnProduce));
+    return w;
+  }
+
+private:
+  abstract_ordered_index *tbl;
+  uint64_t id;
+  bool consumer;
+  uint64_t ctr;
+};
+
+class queue_table_loader : public bench_loader {
+public:
+  queue_table_loader(unsigned long seed,
+                     abstract_db *db,
+                     const map<string, abstract_ordered_index *> &open_tables)
+    : bench_loader(seed, db, open_tables)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    abstract_ordered_index *tbl = open_tables.at("table");
+    try {
+      // load
+      const size_t batchsize = (db->txn_max_batch_size() == -1) ?
+        10000 : db->txn_max_batch_size();
+      ALWAYS_ASSERT(batchsize > 0);
+      const size_t nbatches = nkeys / batchsize;
+      for (size_t id = 0; id < nthreads / 2; id++) {
+        if (nbatches == 0) {
+          void *txn = db->new_txn(txn_flags, arena, txn_buf());
+          for (size_t j = 0; j < nkeys; j++) {
+            const string k = queue_key(id, j);
+            const string &v = queue_values;
+            tbl->insert(txn, k, v);
+          }
+          if (verbose)
+            cerr << "batch 1/1 done" << endl;
+          ALWAYS_ASSERT(db->commit_txn(txn));
+        } else {
+          for (size_t i = 0; i < nbatches; i++) {
+            size_t keyend = (i == nbatches - 1) ? nkeys : (i + 1) * batchsize;
+            void *txn = db->new_txn(txn_flags, arena, txn_buf());
+            for (size_t j = i * batchsize; j < keyend; j++) {
+              const string k = queue_key(id, j);
+              const string &v = queue_values;
+              tbl->insert(txn, k, v);
+            }
+            if (verbose)
+              cerr << "batch " << (i + 1) << "/" << nbatches << " done" << endl;
+            ALWAYS_ASSERT(db->commit_txn(txn));
+          }
+        }
+      }
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      // shouldn't abort on loading!
+      ALWAYS_ASSERT(false);
+    }
+    if (verbose)
+      cerr << "[INFO] finished loading table" << endl;
+  }
+};
+
+class queue_bench_runner : public bench_runner {
+public:
+  queue_bench_runner(abstract_db *db, bool write_only)
+    : bench_runner(db), write_only(write_only)
+  {
+    open_tables["table"] = db->open_index("table", queue_values.size());
+  }
+
+protected:
+  virtual vector<bench_loader *>
+  make_loaders()
+  {
+    vector<bench_loader *> ret;
+    ret.push_back(new queue_table_loader(0, db, open_tables));
+    return ret;
+  }
+
+  virtual vector<bench_worker *>
+  make_workers()
+  {
+    fast_random r(8544290);
+    vector<bench_worker *> ret;
+    if (write_only) {
+      for (size_t i = 0; i < nthreads; i++)
+        ret.push_back(
+          new queue_worker(
+            i, r.next(), db, open_tables,
+            &barrier_a, &barrier_b, i, false));
+    } else {
+      ALWAYS_ASSERT(nthreads >= 2);
+      if (verbose && (nthreads % 2))
+        cerr << "queue_bench_runner: odd number of workers given" << endl;
+      for (size_t i = 0; i < nthreads / 2; i++) {
+        ret.push_back(
+          new queue_worker(
+            i, r.next(), db, open_tables,
+            &barrier_a, &barrier_b, i, true));
+        ret.push_back(
+          new queue_worker(
+            i + 1, r.next(), db, open_tables,
+            &barrier_a, &barrier_b, i, false));
+      }
+    }
+    return ret;
+  }
+
+private:
+  bool write_only;
+};
+
+void
+queue_do_test(abstract_db *db, int argc, char **argv)
+{
+  nkeys = size_t(scale_factor * 1000.0);
+  ALWAYS_ASSERT(nkeys > 0);
+  queue_bench_runner r(db, true);
+  r.run();
+}
diff --git a/silo/benchmarks/results/NOTES.txt b/silo/benchmarks/results/NOTES.txt
new file mode 100644 (file)
index 0000000..427ef5b
--- /dev/null
@@ -0,0 +1,19 @@
+Exp notes:
+
+istc3-8-1-13.py: 40 ms ticker, full persistence
+
+istc3-8-1-13_fake_writes.py: same as itsc3-8-1-13.py, except --log-fake-writes is used
+
+istc3-8-1-13_log_reduce_size.py: same as istc3-8-1-13.py, except LOGGER_UNSAFE_REDUCE_BUFFER_SIZE is defined
+
+istc3-8-1-13_log_reduce_size_nofsync.py: same as istc3-8-1-13_log_reduce_size.py, except --log-nofsync is used
+
+istc3-8-1-13_fake_writes_stride.py: same as istc3-8-1-13_fake_writes.py, except LOGGER_STRIDE_OVER_BUFFER is defined and stridelen=(CACHELINE_SIZE/2)
+
+istc3-8-1-13_fake_writes_stride1.py: same as istc3-8-1-13_fake_writes_stride.py, except stridelen=1
+
+istc3-8-1-13_fake_compress.py: same as istc3-8-1-13.py, except LOGGER_UNSAFE_FAKE_COMPRESSION is enabled
+
+istc3-8-1-13_compress.py: same as istc3-8-1-13.py, except --log-compress is used
+
+istc3-8-1-13_newbench.py: same as istc3-8-1-13.py, except we use 'new-benchmarks/dbtest' (delta encoding)
diff --git a/silo/benchmarks/results/ben-3-8-13.py b/silo/benchmarks/results/ben-3-8-13.py
new file mode 100644 (file)
index 0000000..c7ce728
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 80, 'threads': 80, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (232976.0, 3.76111)), ({'scale_factor': 72, 'threads': 72, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (254892.0, 3.73888)), ({'scale_factor': 64, 'threads': 64, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (242310.0, 3.46666)), ({'scale_factor': 56, 'threads': 56, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (237052.0, 3.57222)), ({'scale_factor': 48, 'threads': 48, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (230557.0, 3.74444)), ({'scale_factor': 40, 'threads': 40, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (234613.0, 3.87222)), ({'scale_factor': 32, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (224310.0, 3.7)), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (190809.0, 2.90555)), ({'scale_factor': 16, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (142130.0, 2.01667)), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (76759.6, 1.21667)), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (38735.2, 0.533333)), ({'scale_factor': 2, 'threads': 2, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (20094.2, 0.305555)), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (10286.7, 0.0))]
diff --git a/silo/benchmarks/results/ben-4-10-13.py b/silo/benchmarks/results/ben-4-10-13.py
new file mode 100644 (file)
index 0000000..91834f1
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '2G', 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(35595.2, 0.0), (35134.1, 0.0), (35668.9, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 10, 'name': 'scale_tpcc', 'numa_memory': '20G', 'threads': 10, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(293841.0, 12.4664), (294454.0, 11.8998), (295441.0, 13.7664)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '40G', 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(573735.0, 26.2659), (571127.0, 24.2994), (572429.0, 25.0326)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 30, 'name': 'scale_tpcc', 'numa_memory': '60G', 'threads': 30, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(842923.0, 37.6319), (841078.0, 37.3323), (848000.0, 39.3986)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 40, 'name': 'scale_tpcc', 'numa_memory': '80G', 'threads': 40, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(1117960.0, 47.8294), (1117570.0, 49.1965), (1117690.0, 47.7618)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 50, 'name': 'scale_tpcc', 'numa_memory': '100G', 'threads': 50, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(1377360.0, 63.2581), (1375480.0, 60.6967), (1380110.0, 58.1962)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 60, 'name': 'scale_tpcc', 'numa_memory': '120G', 'threads': 60, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(1606770.0, 68.7615), (1625400.0, 68.9893), (1578890.0, 65.9616)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 70, 'name': 'scale_tpcc', 'numa_memory': '140G', 'threads': 70, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(1788150.0, 74.5197), (1770310.0, 75.1934), (1779690.0, 73.7555)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 80, 'name': 'scale_tpcc', 'numa_memory': '160G', 'threads': 80, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(1743840.0, 69.416), (1838940.0, 76.984), (1749190.0, 71.3892)]),({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 75, 'name': 'scale_tpcc', 'numa_memory': '150G', 'threads': 75, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(1823040.0, 75.6186), (1826750.0, 77.4802), (1821720.0, 74.7569)])]
diff --git a/silo/benchmarks/results/istc11-3-13-13.py b/silo/benchmarks/results/istc11-3-13-13.py
new file mode 100644 (file)
index 0000000..0c389fa
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 32, 'threads': 32, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (446207.0, 0.0)), ({'scale_factor': 32, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (238640.0, 3.41666)), ({'scale_factor': 28, 'threads': 28, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (382193.0, 0.0)), ({'scale_factor': 28, 'threads': 28, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (256238.0, 3.48332)), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (341648.0, 0.0)), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (225791.0, 3.16666)), ({'scale_factor': 20, 'threads': 20, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (299640.0, 0.0)), ({'scale_factor': 20, 'threads': 20, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (194373.0, 2.46666)), ({'scale_factor': 16, 'threads': 16, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (243529.0, 0.0)), ({'scale_factor': 16, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (159408.0, 2.16666)), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (185722.0, 0.0)), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (121866.0, 1.94999)), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (135299.0, 0.0)), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (85131.2, 0.966664)), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (73284.6, 0.0)), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (42715.4, 0.683332)), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (23663.7, 0.0)), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (11229.9, 0.0))]
diff --git a/silo/benchmarks/results/istc11-3-14-13.py b/silo/benchmarks/results/istc11-3-14-13.py
new file mode 100644 (file)
index 0000000..4e4cf30
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1000, 'threads': 1, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (559456.0, 0.0)), ({'scale_factor': 1000, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (208605.0, 0.0)), ({'scale_factor': 4000, 'threads': 4, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (1772190.0, 0.0)), ({'scale_factor': 4000, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (869710.0, 0.0)), ({'scale_factor': 8000, 'threads': 8, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (3070970.0, 0.0)), ({'scale_factor': 8000, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (1703070.0, 0.0)), ({'scale_factor': 12000, 'threads': 12, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (3899520.0, 0.0)), ({'scale_factor': 12000, 'threads': 12, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (2412830.0, 0.0)), ({'scale_factor': 16000, 'threads': 16, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4281320.0, 0.0)), ({'scale_factor': 16000, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (2834350.0, 0.0)), ({'scale_factor': 20000, 'threads': 20, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4407900.0, 0.0)), ({'scale_factor': 20000, 'threads': 20, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (3180140.0, 0.0)), ({'scale_factor': 24000, 'threads': 24, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4469700.0, 0.0)), ({'scale_factor': 24000, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (3413230.0, 0.0)), ({'scale_factor': 28000, 'threads': 28, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4511650.0, 0.0)), ({'scale_factor': 28000, 'threads': 28, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (4222430.0, 0.0)), ({'scale_factor': 32000, 'threads': 32, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4969290.0, 0.0)), ({'scale_factor': 32000, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (3812400.0, 0.0166666)), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (28786.0, 0.0)), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (11423.1, 0.0)), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (101236.0, 0.0)), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (43745.5, 0.583332)), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (200612.0, 0.0)), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (88838.5, 1.24999)), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (287726.0, 0.0)), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (124150.0, 1.63333)), ({'scale_factor': 16, 'threads': 16, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (373223.0, 0.0)), ({'scale_factor': 16, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (165641.0, 2.25)), ({'scale_factor': 20, 'threads': 20, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (469325.0, 0.0)), ({'scale_factor': 20, 'threads': 20, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (203462.0, 2.63333)), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (542229.0, 0.0)), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (237899.0, 2.84999)), ({'scale_factor': 28, 'threads': 28, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (604902.0, 0.0)), ({'scale_factor': 28, 'threads': 28, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (268282.0, 3.06666)), ({'scale_factor': 32, 'threads': 32, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (649687.0, 0.0)), ({'scale_factor': 32, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (245272.0, 2.76666))]
diff --git a/silo/benchmarks/results/istc11-3-16-13.py b/silo/benchmarks/results/istc11-3-16-13.py
new file mode 100644 (file)
index 0000000..bcc9780
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1000, 'threads': 1, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (455923.0, 0.0)), ({'scale_factor': 1000, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (392189.0, 0.0)), ({'scale_factor': 4000, 'threads': 4, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (1837830.0, 0.0)), ({'scale_factor': 4000, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (1386150.0, 0.0)), ({'scale_factor': 8000, 'threads': 8, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (3117300.0, 0.0)), ({'scale_factor': 8000, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (2378310.0, 0.0)), ({'scale_factor': 12000, 'threads': 12, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (3941100.0, 0.0)), ({'scale_factor': 12000, 'threads': 12, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (3129000.0, 0.0)), ({'scale_factor': 16000, 'threads': 16, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4299420.0, 0.0)), ({'scale_factor': 16000, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (3477480.0, 0.0)), ({'scale_factor': 20000, 'threads': 20, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4436690.0, 0.0)), ({'scale_factor': 20000, 'threads': 20, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (3591450.0, 0.0)), ({'scale_factor': 24000, 'threads': 24, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4492090.0, 0.0)), ({'scale_factor': 24000, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (3583380.0, 0.0)), ({'scale_factor': 28000, 'threads': 28, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4523280.0, 0.0)), ({'scale_factor': 28000, 'threads': 28, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (3737430.0, 0.0)), ({'scale_factor': 32000, 'threads': 32, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, (4557360.0, 0.0)), ({'scale_factor': 32000, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, (4139190.0, 0.0)), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (28194.3, 0.0)), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (15643.4, 0.0)), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (103030.0, 0.0)), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (58260.7, 0.866664)), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (199311.0, 0.0)), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (115993.0, 1.83333)), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (288046.0, 0.0)), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (161253.0, 2.68333)), ({'scale_factor': 16, 'threads': 16, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (369982.0, 0.0)), ({'scale_factor': 16, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (214555.0, 3.24999)), ({'scale_factor': 20, 'threads': 20, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (458774.0, 0.0)), ({'scale_factor': 20, 'threads': 20, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (260806.0, 3.78332)), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (544124.0, 0.0)), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (296078.0, 4.59998)), ({'scale_factor': 28, 'threads': 28, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (616619.0, 0.0)), ({'scale_factor': 28, 'threads': 28, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (320886.0, 5.46665)), ({'scale_factor': 32, 'threads': 32, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, (646355.0, 0.0)), ({'scale_factor': 32, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (295248.0, 4.09999))]
diff --git a/silo/benchmarks/results/istc11-3-18-13.py b/silo/benchmarks/results/istc11-3-18-13.py
new file mode 100644 (file)
index 0000000..dfdcdb0
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1000, 'threads': 1, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(436120.0, 0.0), (418455.0, 0.0), (450830.0, 0.0)]), ({'scale_factor': 1000, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(278658.0, 0.0), (358839.0, 0.0), (326228.0, 0.0)]), ({'scale_factor': 4000, 'threads': 4, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(1830520.0, 0.0), (1811250.0, 0.0), (1811350.0, 0.0)]), ({'scale_factor': 4000, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(1155860.0, 0.0), (1131700.0, 0.0), (1180550.0, 0.0)]), ({'scale_factor': 8000, 'threads': 8, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(3094700.0, 0.0), (3015630.0, 0.0), (3017340.0, 0.0)]), ({'scale_factor': 8000, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(2166540.0, 0.0), (2206350.0, 0.0), (2134760.0, 0.0)]), ({'scale_factor': 12000, 'threads': 12, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(3916050.0, 0.0), (3915280.0, 0.0), (3921000.0, 0.0)]), ({'scale_factor': 12000, 'threads': 12, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(2957350.0, 0.0), (2979110.0, 0.0), (2964590.0, 0.0)]), ({'scale_factor': 16000, 'threads': 16, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(4322820.0, 0.0), (4304250.0, 0.0), (4326410.0, 0.0)]), ({'scale_factor': 16000, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(3465220.0, 0.0), (3494080.0, 0.0), (3851230.0, 0.0)]), ({'scale_factor': 20000, 'threads': 20, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(4448970.0, 0.0), (4432920.0, 0.0), (4451670.0, 0.0)]), ({'scale_factor': 20000, 'threads': 20, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(4660560.0, 0.0), (3694510.0, 0.0), (3621290.0, 0.0)]), ({'scale_factor': 24000, 'threads': 24, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(4504350.0, 0.0), (6451530.0, 0.0), (4504910.0, 0.0)]), ({'scale_factor': 24000, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(3701890.0, 0.0), (4056880.0, 0.0), (5237840.0, 0.0)]), ({'scale_factor': 28000, 'threads': 28, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(4549070.0, 0.0), (4551480.0, 0.0), (7400250.0, 0.0)]), ({'scale_factor': 28000, 'threads': 28, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(3981770.0, 0.0), (3546130.0, 0.0), (3529950.0, 0.0)]), ({'scale_factor': 32000, 'threads': 32, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(7948560.0, 0.0), (7945880.0, 0.0), (4566300.0, 0.0)]), ({'scale_factor': 32000, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(3898920.0, 0.0166666), (3907880.0, 0.0), (3321670.0, 0.0)]), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, [(30405.5, 0.0), (30499.0, 0.0), (30028.5, 0.0)]), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(16356.8, 0.433332), (15986.1, 0.283333), (16439.4, 0.399999)]), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, [(111137.0, 0.0), (108277.0, 0.0), (109730.0, 0.0)]), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(66322.5, 1.75), (66222.7, 2.03333), (63792.9, 1.64999)]), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, [(214859.0, 0.0), (214484.0, 0.0), (215524.0, 0.0)]), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(128448.0, 3.14999), (129981.0, 3.06666), (129171.0, 3.04999)]), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, [(300235.0, 0.0), (306062.0, 0.0), (307826.0, 0.0)]), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(187376.0, 4.38332), (188755.0, 4.48332), (187961.0, 4.49999)]), ({'scale_factor': 16, 'threads': 16, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, [(404846.0, 0.0), (396609.0, 0.0), (407262.0, 0.0)]), ({'scale_factor': 16, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(249271.0, 5.71665), (246597.0, 4.98331), (243792.0, 5.83332)]), ({'scale_factor': 20, 'threads': 20, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, [(496558.0, 0.0), (503799.0, 0.0), (503256.0, 0.0)]), ({'scale_factor': 20, 'threads': 20, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(304026.0, 6.29998), (304226.0, 6.81666), (295475.0, 5.99998)]), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, [(587429.0, 0.0), (584843.0, 0.0), (590804.0, 0.0)]), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(351511.0, 6.81664), (361679.0, 8.11663), (358653.0, 7.74997)]), ({'scale_factor': 28, 'threads': 28, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, [(670600.0, 0.0), (662400.0, 0.0), (668818.0, 0.0)]), ({'scale_factor': 28, 'threads': 28, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(409829.0, 8.28332), (408729.0, 8.54998), (406639.0, 7.51665)]), ({'scale_factor': 32, 'threads': 32, 'txn_flags': 1, 'db': 'kvdb', 'bench': 'tpcc'}, [(694132.0, 0.0), (701495.0, 0.0), (707058.0, 0.0)]), ({'scale_factor': 32, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(390649.0, 5.61666), (390995.0, 6.08332), (387345.0, 5.94999)])]
diff --git a/silo/benchmarks/results/istc11-3-21-13.py b/silo/benchmarks/results/istc11-3-21-13.py
new file mode 100644 (file)
index 0000000..e63b1a4
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'bench_opts': '', 'scale_factor': 320000, 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 1}, [(341977.0, 0.0), (367991.0, 0.0), (342608.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 4}, [(1565190.0, 0.0), (1565550.0, 0.0), (1563210.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 8}, [(3193760.0, 0.0), (3182240.0, 0.0), (3188560.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 12}, [(4780000.0, 0.0), (4767620.0, 0.0), (4771070.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 16}, [(6332840.0, 0.0), (6328970.0, 0.0), (6334500.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 20}, [(7872050.0, 0.0), (7871110.0, 0.0), (7845550.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 24}, [(9363600.0, 0.0), (9368290.0, 0.0), (9366990.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 28}, [(10832500.0, 0.0), (10770300.0, 0.0), (10818600.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 32}, [(11856400.0, 0.0), (11855900.0, 0.0), (11865400.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 1}, [(323784.0, 0.0), (321806.0, 0.0), (322916.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 4}, [(1385090.0, 0.0), (1391820.0, 0.0), (1344820.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 8}, [(2827340.0, 0.0), (2732100.0, 0.0), (2758880.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 12}, [(4209050.0, 0.0), (4182500.0, 0.0), (4193630.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 16}, [(5485600.0, 0.0), (5500570.0, 0.0333328), (5532810.0, 0.0166665)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 20}, [(6959520.0, 0.0), (6969320.0, 0.0166664), (6948200.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 24}, [(8319120.0, 0.0), (8340050.0, 0.0333328), (8297860.0, 0.0333328)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 28}, [(9631680.0, 0.0), (9588000.0, 0.0333328), (9622120.0, 0.0333327)]), ({'bench_opts': '', 'scale_factor': 320000, 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 32}, [(10263300.0, 0.0166629), (10300700.0, 0.0333262), (10163800.0, 0.0499888)]), ({'bench_opts': '--new-order-remote-item-pct 0', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(440703.0, 0.0), (435462.0, 0.0), (449485.0, 0.0)]), ({'bench_opts': '--new-order-remote-item-pct 1', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(434779.0, 68.3486), (438273.0, 69.6653), (442467.0, 69.2987)]), ({'bench_opts': '--new-order-remote-item-pct 2', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(445738.0, 142.23), (435539.0, 136.43), (430208.0, 135.364)]), ({'bench_opts': '--new-order-remote-item-pct 3', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(435363.0, 202.596), (431492.0, 204.33), (447837.0, 213.629)]), ({'bench_opts': '--new-order-remote-item-pct 4', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(430948.0, 266.161), (441064.0, 273.478), (431624.0, 272.662)]), ({'bench_opts': '--new-order-remote-item-pct 5', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(436018.0, 340.293), (438748.0, 339.76), (438044.0, 338.56)]), ({'bench_opts': '--new-order-remote-item-pct 6', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(431446.0, 400.741), (434324.0, 402.438), (436072.0, 405.976)]), ({'bench_opts': '--new-order-remote-item-pct 7', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(429735.0, 463.173), (440026.0, 472.789), (426122.0, 457.674)]), ({'bench_opts': '--new-order-remote-item-pct 8', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(425216.0, 521.456), (438042.0, 535.771), (430484.0, 529.554)]), ({'bench_opts': '--new-order-remote-item-pct 9', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(439448.0, 602.687), (436484.0, 596.338), (442301.0, 608.353)]), ({'bench_opts': '--new-order-remote-item-pct 10', 'scale_factor': 28, 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(434582.0, 661.104), (424904.0, 634.071), (434322.0, 665.07)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(758259.0, 0.0), (771913.0, 0.0), (763892.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(645656.0, 0.0), (650949.0, 0.0), (627824.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(536405.0, 0.0), (531694.0, 0.0), (537912.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(443490.0, 0.0), (432826.0, 0.0), (441318.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(368602.0, 0.0), (352058.0, 0.0), (368116.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(306356.0, 0.0), (308941.0, 0.0), (310371.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(263430.0, 0.0), (250817.0, 0.0), (263369.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(227992.0, 0.0), (216511.0, 0.0), (226312.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(197707.0, 0.0), (196394.0, 0.0), (197256.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(177286.0, 0.0), (177743.0, 0.0), (175348.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'scale_factor': 28, 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(157529.0, 0.0), (159431.0, 0.0), (158633.0, 0.0)])]
diff --git a/silo/benchmarks/results/istc11-3-22-13.py b/silo/benchmarks/results/istc11-3-22-13.py
new file mode 100644 (file)
index 0000000..a51eeed
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 1}, [(344319.0, 0.0), (354514.0, 0.0), (342661.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 4}, [(1572150.0, 0.0), (1562970.0, 0.0), (1565110.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 8}, [(3194780.0, 0.0), (3192960.0, 0.0), (3188600.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 12}, [(4782170.0, 0.0), (4773360.0, 0.0), (4767710.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 16}, [(6333480.0, 0.0), (6326960.0, 0.0), (6349480.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 20}, [(7879620.0, 0.0), (7876960.0, 0.0), (7868610.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 24}, [(9375300.0, 0.0), (9369260.0, 0.0), (9355660.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 28}, [(10824800.0, 0.0), (10814600.0, 0.0), (10805100.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 32}, [(11893900.0, 0.0), (11970300.0, 0.0), (11848300.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 1}, [(344057.0, 0.0), (330148.0, 0.0), (333267.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 4}, [(1501350.0, 0.0), (1481720.0, 0.0), (1491190.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 8}, [(3041380.0, 0.0), (3022010.0, 0.0), (3054820.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 12}, [(4566850.0, 0.0), (4575880.0, 0.0), (4575180.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 16}, [(6081740.0, 0.0), (6057220.0, 0.0166665), (6088750.0, 0.0166665)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 20}, [(7521460.0, 0.0), (7519680.0, 0.0), (7541440.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 24}, [(8985640.0, 0.0), (8971310.0, 0.0), (8973110.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 28}, [(10429500.0, 0.0833324), (10437100.0, 0.0), (10428400.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 32}, [(11046300.0, 0.0), (11080000.0, 0.0166646), (11123100.0, 0.016663)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(505953.0, 0.0), (508367.0, 0.0), (531330.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(539975.0, 83.5486), (539597.0, 83.1168), (530897.0, 84.1982)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(529145.0, 163.396), (537027.0, 169.68), (531671.0, 165.198)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(526006.0, 243.896), (531250.0, 245.114), (515408.0, 241.297)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(526523.0, 326.51), (519784.0, 320.029), (534266.0, 329.362)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(533564.0, 408.844), (530472.0, 406.311), (532288.0, 407.412)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(525542.0, 479.861), (533309.0, 491.876), (536782.0, 486.96)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(531937.0, 564.39), (518706.0, 553.174), (526912.0, 561.458)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(523326.0, 631.139), (521117.0, 626.808), (519460.0, 629.224)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(531315.0, 720.686), (518967.0, 704.337), (518784.0, 696.975)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(525105.0, 781.123), (517552.0, 777.807), (527545.0, 785.624)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(832667.0, 0.0), (812928.0, 0.0), (828543.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(664056.0, 0.0), (671050.0, 0.0), (700404.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(562333.0, 0.0), (586266.0, 0.0), (560772.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(484997.0, 0.0), (458707.0, 0.0), (482848.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(383788.0, 0.0), (385848.0, 0.0), (383601.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(318259.0, 0.0), (318550.0, 0.0), (320540.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(274341.0, 0.0), (284535.0, 0.0), (266393.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(243409.0, 0.0), (235239.0, 0.0), (245459.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(212585.0, 0.0), (206563.0, 0.0), (202268.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(191315.0, 0.0), (191117.0, 0.0), (183958.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(171544.0, 0.0), (161109.0, 0.0), (161529.0, 0.0)]), ({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 8}, [(222715.0, 0.0), (232206.0, 0.0), (223868.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 8}, [(193922.0, 9.41657), (192451.0, 9.36656), (194744.0, 9.98324)]), ({'bench_opts': '', 'scale_factor': 12, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 8}, [(190925.0, 5.73328), (190402.0, 5.54995), (184899.0, 5.91661)]), ({'bench_opts': '', 'scale_factor': 16, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 8}, [(186043.0, 4.2333), (191044.0, 4.29997), (193370.0, 4.98329)]), ({'bench_opts': '', 'scale_factor': 20, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 8}, [(192382.0, 3.21664), (193473.0, 3.53331), (188681.0, 3.49997)]), ({'bench_opts': '', 'scale_factor': 24, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 8}, [(194884.0, 2.89997), (192628.0, 3.09996), (190728.0, 3.04998)]), ({'bench_opts': '', 'scale_factor': 28, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 8}, [(188494.0, 2.39998), (190250.0, 2.21664), (188596.0, 2.66664)]), ({'bench_opts': '', 'scale_factor': 32, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 8}, [(187125.0, 2.14998), (190069.0, 2.14998), (182439.0, 2.01665)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(171620.0, 2134.06), (173529.0, 2186.03), (173225.0, 2138.29)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(169040.0, 2289.85), (171705.0, 2262.18), (170010.0, 2311.37)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(171233.0, 2310.86), (169888.0, 2288.3), (169092.0, 2317.81)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(167489.0, 2295.77), (168658.0, 2328.69), (167674.0, 2283.77)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(166622.0, 2321.01), (168422.0, 2335.13), (167353.0, 2352.83)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(166810.0, 2291.52), (167038.0, 2285.16), (167169.0, 2331.86)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(150297.0, 13787.1), (150142.0, 13767.5), (150083.0, 13777.7)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(148402.0, 15224.0), (147885.0, 15174.4), (147474.0, 15163.6)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(146138.0, 16536.9), (147086.0, 16641.2), (146483.0, 16522.5)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(144554.0, 17776.3), (142998.0, 17571.6), (144150.0, 17711.9)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(142847.0, 18846.9), (141997.0, 18745.8), (142227.0, 18762.5)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(140923.0, 19884.2), (140449.0, 19816.0), (140712.0, 19868.4)])]
diff --git a/silo/benchmarks/results/istc11-3-23-13.py b/silo/benchmarks/results/istc11-3-23-13.py
new file mode 100644 (file)
index 0000000..a1da02a
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'bench_opts': '--enable-separate-tree-per-partition --enable-partition-locks', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'kvdb', 'bench': 'tpcc', 'par_load': True, 'threads': 8}, [(222921.0, 0.0), (227359.0, 0.0), (229843.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 8}, [(195168.0, 9.73322), (194644.0, 9.84993), (194702.0, 9.44992)]), ({'bench_opts': '', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 12}, [(237444.0, 37277.3), (234301.0, 36610.4), (236327.0, 36780.5)]), ({'bench_opts': '', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(284964.0, 73789.8), (290414.0, 74756.3), (276865.0, 71355.9)]), ({'bench_opts': '', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 20}, [(301047.0, 123422.0), (282959.0, 115540.0), (287034.0, 115244.0)]), ({'bench_opts': '', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 24}, [(296876.0, 159299.0), (298336.0, 159218.0), (302464.0, 162004.0)]), ({'bench_opts': '', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(246765.0, 157871.0), (270421.0, 171978.0), (254094.0, 160962.0)]), ({'bench_opts': '', 'scale_factor': 8, 'name': 'multipart:cpu', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 32}, [(230114.0, 167878.0), (226383.0, 165009.0), (227876.0, 165982.0)])]
diff --git a/silo/benchmarks/results/istc11-3-26-13.py b/silo/benchmarks/results/istc11-3-26-13.py
new file mode 100644 (file)
index 0000000..febf830
--- /dev/null
@@ -0,0 +1,4 @@
+RESULTS = [({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 1}, [(368896.0, 0.0), (370812.0, 0.0), (340529.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 4}, [(1557670.0, 0.0), (1557680.0, 0.0), (1558510.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 8}, [(3182470.0, 0.0), (3181050.0, 0.0), (3177240.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 12}, [(4754210.0, 0.0), (4758890.0, 0.0), (4768010.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 16}, [(6327650.0, 0.0), (6335970.0, 0.0), (6323000.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 20}, [(7651490.0, 0.0), (7859290.0, 0.0), (7851350.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 24}, [(9338320.0, 0.0), (9334100.0, 0.0), (9337990.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 28}, [(10809400.0, 0.0), (10762300.0, 0.0), (10782400.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'threads': 32}, [(11635500.0, 0.0), (11909600.0, 0.0), (11829800.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 1}, [(362783.0, 0.0), (362939.0, 0.0), (362555.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 4}, [(1530310.0, 0.0), (1547110.0, 0.0), (1562350.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 8}, [(3147020.0, 0.0), (3154120.0, 0.0), (3156800.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 12}, [(4703160.0, 0.0), (4718940.0, 0.0), (4615850.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 16}, [(6252960.0, 0.0), (6272330.0, 0.0), (6262620.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 20}, [(7775340.0, 0.033333), (7805900.0, 0.0), (7790730.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 24}, [(9292160.0, 0.0), (9296380.0, 0.0), (9289300.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 28}, [(10734600.0, 0.0), (10742600.0, 0.0), (10724200.0, 0.0)]), ({'bench_opts': '', 'scale_factor': 320000, 'name': 'scale', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'threads': 32}, [(11442700.0, 0.0), (11409800.0, 0.0), (11418000.0, 0.0166631)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(559985.0, 0.0), (554159.0, 0.0), (560124.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(563669.0, 86.8307), (550329.0, 81.4322), (558763.0, 83.9809)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(552469.0, 168.415), (560887.0, 171.246), (558669.0, 169.694)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(536695.0, 248.114), (559353.0, 256.428), (549388.0, 252.111)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(551636.0, 332.724), (557507.0, 332.662), (554321.0, 331.291)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(553094.0, 415.344), (553564.0, 411.806), (556768.0, 413.706)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(539732.0, 483.26), (551578.0, 493.237), (523114.0, 471.342)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(550531.0, 569.523), (539723.0, 562.572), (551360.0, 572.741)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(542719.0, 643.342), (549563.0, 643.558), (546139.0, 648.14)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(548901.0, 730.787), (540353.0, 715.709), (542625.0, 724.837)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 28}, [(547367.0, 796.404), (550785.0, 803.884), (539808.0, 785.229)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(842114.0, 0.0), (838839.0, 0.0), (845235.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(667442.0, 0.0), (665713.0, 0.0), (675465.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(536509.0, 0.0), (539068.0, 0.0), (546692.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(437646.0, 0.0), (437500.0, 0.0), (444910.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(342401.0, 0.0), (348083.0, 0.0), (351640.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(307548.0, 0.0), (311420.0, 0.0), (308924.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(255155.0, 0.0), (265231.0, 0.0), (264143.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(232640.0, 0.0), (230824.0, 0.0), (228088.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(204345.0, 0.0), (206614.0, 0.0), (192869.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(180656.0, 0.0), (185980.0, 0.0), (184886.0, 0.0)]), ({'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'scale_factor': 28, 'name': 'multipart:pct', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': True, 'threads': 28}, [(166456.0, 0.0), (160836.0, 0.0), (161666.0, 0.0)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(178026.0, 2137.24), (178581.0, 2116.92), (177426.0, 2175.65)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(176774.0, 2222.03), (176332.0, 2189.11), (176691.0, 2223.81)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(174556.0, 2272.04), (175010.0, 2263.7), (172122.0, 2313.65)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(172754.0, 2280.72), (173202.0, 2300.94), (173661.0, 2303.13)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(173194.0, 2308.66), (171923.0, 2312.56), (172893.0, 2318.47)]), ({'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(172705.0, 2295.76), (171586.0, 2302.47), (172432.0, 2308.46)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(155424.0, 14688.1), (154900.0, 14657.8), (153882.0, 14555.3)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(152169.0, 16301.0), (151600.0, 16152.8), (153404.0, 16359.3)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(151897.0, 17950.7), (150753.0, 17881.1), (148796.0, 17610.6)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(146694.0, 18958.8), (146309.0, 18870.3), (146731.0, 18757.5)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(146141.0, 20348.6), (145702.0, 20284.8), (146323.0, 20411.8)]), ({'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'scale_factor': 8, 'name': 'readonly', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'threads': 16}, [(142882.0, 21250.3), (144235.0, 21529.0), (143130.0, 21315.2)])]
+
+
+
diff --git a/silo/benchmarks/results/istc11-4-10-13.py b/silo/benchmarks/results/istc11-4-10-13.py
new file mode 100644 (file)
index 0000000..6dfc5f3
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(804585.0, 0.0), (801148.0, 0.0), (807541.0, 0.0)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(801033.0, 122.895), (799989.0, 122.113), (803801.0, 122.846)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(799259.0, 242.918), (797091.0, 242.774), (794599.0, 242.152)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(790597.0, 357.701), (790143.0, 361.526), (789851.0, 355.109)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(784611.0, 470.576), (785424.0, 475.99), (784797.0, 475.866)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(776102.0, 578.822), (782605.0, 587.097), (784011.0, 591.513)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(775516.0, 695.638), (777636.0, 694.746), (774385.0, 692.662)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(776174.0, 812.69), (769456.0, 800.863), (769434.0, 798.308)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(772895.0, 918.994), (772724.0, 916.723), (775404.0, 916.713)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(766380.0, 1014.59), (772520.0, 1021.4), (769013.0, 1013.95)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(764523.0, 1123.01), (769060.0, 1128.28), (762434.0, 1117.84)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(1094020.0, 0.0), (1110840.0, 0.0), (1075520.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(826353.0, 0.0), (852781.0, 0.0), (845913.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(671993.0, 0.0), (706678.0, 0.0), (656195.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(550780.0, 0.0), (554407.0, 0.0), (536970.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(440937.0, 0.0), (458112.0, 0.0), (460816.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(389108.0, 0.0), (391405.0, 0.0), (390824.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(337935.0, 0.0), (336006.0, 0.0), (342643.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(300376.0, 0.0), (288345.0, 0.0), (305941.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(261991.0, 0.0), (269196.0, 0.0), (261985.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(237309.0, 0.0), (235877.0, 0.0), (231598.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(214308.0, 0.0), (221298.0, 0.0), (213673.0, 0.0)])]
diff --git a/silo/benchmarks/results/istc11-5-18-13-tpcc-chains.txt b/silo/benchmarks/results/istc11-5-18-13-tpcc-chains.txt
new file mode 100644 (file)
index 0000000..d4af9af
--- /dev/null
@@ -0,0 +1,108 @@
+chain_1 : 80781843
+chain_10 : 69583
+chain_100 : 30
+chain_101 : 27
+chain_102 : 21
+chain_103 : 14
+chain_104 : 14
+chain_105 : 5
+chain_106 : 5
+chain_107 : 3
+chain_108 : 1
+chain_11 : 54797
+chain_12 : 50165
+chain_13 : 53632
+chain_14 : 62206
+chain_15 : 72109
+chain_16 : 79712
+chain_17 : 82721
+chain_18 : 80659
+chain_19 : 73692
+chain_2 : 210630340
+chain_20 : 62250
+chain_21 : 50632
+chain_22 : 37955
+chain_23 : 28385
+chain_24 : 20306
+chain_25 : 14883
+chain_26 : 11299
+chain_27 : 9796
+chain_28 : 9530
+chain_29 : 10319
+chain_3 : 229266
+chain_30 : 12767
+chain_31 : 15921
+chain_32 : 20561
+chain_33 : 26589
+chain_34 : 33385
+chain_35 : 39834
+chain_36 : 46231
+chain_37 : 50744
+chain_38 : 52893
+chain_39 : 51505
+chain_4 : 184648
+chain_40 : 47786
+chain_41 : 42079
+chain_42 : 34178
+chain_43 : 27086
+chain_44 : 19801
+chain_45 : 14068
+chain_46 : 9711
+chain_47 : 6562
+chain_48 : 4925
+chain_49 : 4063
+chain_5 : 159018
+chain_50 : 3799
+chain_51 : 4126
+chain_52 : 5216
+chain_53 : 6829
+chain_54 : 10044
+chain_55 : 15948
+chain_56 : 26110
+chain_57 : 38423
+chain_58 : 49675
+chain_59 : 53167
+chain_6 : 148064
+chain_60 : 45919
+chain_61 : 33058
+chain_62 : 33117
+chain_63 : 36589
+chain_64 : 31232
+chain_65 : 21209
+chain_66 : 12718
+chain_67 : 8052
+chain_68 : 5531
+chain_69 : 3822
+chain_7 : 136580
+chain_70 : 2612
+chain_71 : 1669
+chain_72 : 959
+chain_73 : 622
+chain_74 : 489
+chain_75 : 456
+chain_76 : 500
+chain_77 : 506
+chain_78 : 499
+chain_79 : 439
+chain_8 : 116110
+chain_80 : 347
+chain_81 : 272
+chain_82 : 199
+chain_83 : 127
+chain_84 : 87
+chain_85 : 50
+chain_86 : 22
+chain_87 : 10
+chain_88 : 8
+chain_89 : 9
+chain_9 : 92385
+chain_90 : 4
+chain_91 : 3
+chain_92 : 8
+chain_93 : 15
+chain_94 : 25
+chain_95 : 27
+chain_96 : 24
+chain_97 : 29
+chain_98 : 39
+chain_99 : 35
diff --git a/silo/benchmarks/results/istc11-5-18-13-ycsb-chains.txt b/silo/benchmarks/results/istc11-5-18-13-ycsb-chains.txt
new file mode 100644 (file)
index 0000000..e59bbe0
--- /dev/null
@@ -0,0 +1,9 @@
+chain_1 : 205228249
+chain_2 : 91357162
+chain_3 : 20138902
+chain_4 : 2929173
+chain_5 : 317410
+chain_6 : 27081
+chain_7 : 1901
+chain_8 : 113
+chain_9 : 9
diff --git a/silo/benchmarks/results/istc11-5-18-13.py b/silo/benchmarks/results/istc11-5-18-13.py
new file mode 100644 (file)
index 0000000..e6c4ec9
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '101G', 'threads': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(620600.0, 0.0), (621420.0, 0.0), (622727.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '101G', 'threads': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(605937.0, 0.0), (606707.0, 0.0), (615925.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '101G', 'threads': 1, 'db': 'kvdb', 'bench': 'ycsb'}, [(518197.0, 0.0), (518351.0, 0.0), (517762.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '101G', 'threads': 1, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(516004.0, 0.0), (515056.0, 0.0), (515394.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '105G', 'threads': 4, 'db': 'kvdb', 'bench': 'ycsb'}, [(2507000.0, 0.0), (2515440.0, 0.0), (2515880.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '105G', 'threads': 4, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(2440110.0, 0.0), (2438170.0, 0.0), (2444660.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '105G', 'threads': 4, 'db': 'kvdb', 'bench': 'ycsb'}, [(2103950.0, 0.0), (2108650.0, 0.0), (2101390.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '105G', 'threads': 4, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(2021630.0, 0.0), (2051260.0, 0.0), (2062360.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '111G', 'threads': 8, 'db': 'kvdb', 'bench': 'ycsb'}, [(4698460.0, 0.0), (4701020.0, 0.0), (4697040.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '111G', 'threads': 8, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(4594410.0, 0.0), (4585480.0, 0.0), (4585570.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '111G', 'threads': 8, 'db': 'kvdb', 'bench': 'ycsb'}, [(3953090.0, 0.0), (3952270.0, 0.0), (3956260.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '111G', 'threads': 8, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(3893480.0, 0.0166664), (3832850.0, 0.0), (3888720.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '116G', 'threads': 12, 'db': 'kvdb', 'bench': 'ycsb'}, [(6420760.0, 0.0), (6438670.0, 0.0), (6433960.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '116G', 'threads': 12, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(6363770.0, 0.0), (6379600.0, 0.0), (6389300.0, 0.0166661)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '116G', 'threads': 12, 'db': 'kvdb', 'bench': 'ycsb'}, [(5401450.0, 0.0), (5407930.0, 0.0), (5392230.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '116G', 'threads': 12, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(5402250.0, 0.0333321), (5376760.0, 0.0166661), (5390240.0, 0.0333322)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '122G', 'threads': 16, 'db': 'kvdb', 'bench': 'ycsb'}, [(8338980.0, 0.0), (8334610.0, 0.0), (8328680.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '122G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(8257480.0, 0.0), (8226600.0, 0.0), (8277980.0, 0.0333317)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '122G', 'threads': 16, 'db': 'kvdb', 'bench': 'ycsb'}, [(6984660.0, 0.0), (6980250.0, 0.0), (6980670.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '122G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(6999110.0, 0.0166657), (6994190.0, 0.0166658), (6998980.0, 0.0166659)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '128G', 'threads': 20, 'db': 'kvdb', 'bench': 'ycsb'}, [(10199100.0, 0.0), (10189900.0, 0.0), (10186000.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '128G', 'threads': 20, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(10127800.0, 0.0), (10161500.0, 0.0), (10109000.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '128G', 'threads': 20, 'db': 'kvdb', 'bench': 'ycsb'}, [(8543690.0, 0.0), (8551340.0, 0.0), (8535590.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '128G', 'threads': 20, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(8579520.0, 0.049996), (8561890.0, 0.0333303), (8576020.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '133G', 'threads': 24, 'db': 'kvdb', 'bench': 'ycsb'}, [(11904400.0, 0.0), (11904800.0, 0.0), (11895800.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '133G', 'threads': 24, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(11820400.0, 0.0), (11836000.0, 0.0166648), (11816400.0, 0.0166651)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '133G', 'threads': 24, 'db': 'kvdb', 'bench': 'ycsb'}, [(9960240.0, 0.0), (9946770.0, 0.0), (9967100.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '133G', 'threads': 24, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(10030400.0, 0.0999886), (10022700.0, 0.0499943), (10023500.0, 0.0166643)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '139G', 'threads': 28, 'db': 'kvdb', 'bench': 'ycsb'}, [(14055700.0, 0.0), (14064700.0, 0.0), (14048200.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '139G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(13991000.0, 0.0), (13987400.0, 0.0), (13974900.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '139G', 'threads': 28, 'db': 'kvdb', 'bench': 'ycsb'}, [(11775200.0, 0.0), (11773500.0, 0.0), (11763400.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '139G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(11841300.0, 0.0333278), (11859000.0, 0.099985), (11843500.0, 0.0499928)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '144G', 'threads': 32, 'db': 'kvdb', 'bench': 'ycsb'}, [(15886600.0, 0.0), (15883900.0, 0.0), (15914600.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '144G', 'threads': 32, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(15709800.0, 0.0166588), (15721400.0, 0.0166599), (15791500.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '144G', 'threads': 32, 'db': 'kvdb', 'bench': 'ycsb'}, [(13298200.0, 0.0), (13284200.0, 0.0), (13316600.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '144G', 'threads': 32, 'db': 'ndb-proto2', 'bench': 'ycsb'}, [(13258000.0, 0.133309), (13318500.0, 0.116649), (13318900.0, 0.14997)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(30985.9, 0.0), (29673.8, 0.0166664), (32070.4, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(112760.0, 5.03329), (112690.0, 5.29995), (113504.0, 4.89996)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(208462.0, 8.96661), (212875.0, 10.0832), (212539.0, 9.33326)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(325661.0, 14.4332), (328256.0, 13.6332), (327681.0, 14.5498)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(430089.0, 19.7165), (421372.0, 18.4498), (432434.0, 17.8832)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(543340.0, 23.1331), (545883.0, 23.9497), (546608.0, 23.5831)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(648722.0, 29.233), (633469.0, 26.3663), (644658.0, 27.2329)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(750691.0, 34.5494), (743953.0, 32.9328), (741685.0, 32.2494)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(810975.0, 35.2974), (813899.0, 36.0823), (803711.0, 35.4822)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(787305.0, 0.0), (783427.0, 0.0), (787891.0, 0.0)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(757735.0, 114.1), (773002.0, 118.563), (772284.0, 117.997)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(765908.0, 233.027), (766292.0, 235.925), (772907.0, 233.54)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(767204.0, 348.752), (757182.0, 345.413), (762173.0, 347.066)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(762240.0, 465.634), (766427.0, 461.286), (764470.0, 463.978)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(758053.0, 569.597), (755356.0, 568.032), (749486.0, 562.348)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(750449.0, 675.393), (760504.0, 671.979), (753789.0, 676.568)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(757352.0, 795.228), (754869.0, 782.752), (733751.0, 770.558)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(750816.0, 890.491), (733219.0, 858.385), (753169.0, 902.858)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(744790.0, 988.435), (748771.0, 990.238), (745841.0, 975.928)]), ({'par_load': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(745825.0, 1092.56), (743188.0, 1079.22), (749344.0, 1090.5)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(1098950.0, 0.0), (1119710.0, 0.0), (1072420.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(889236.0, 0.0), (898120.0, 0.0), (900607.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(722596.0, 0.0), (711485.0, 0.0), (712563.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(582426.0, 0.0), (578095.0, 0.0), (583767.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(482736.0, 0.0), (483404.0, 0.0), (487373.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(414524.0, 0.0), (415143.0, 0.0), (415969.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(350162.0, 0.0), (352123.0, 0.0), (352476.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(311676.0, 0.0), (311529.0, 0.0), (307683.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(274936.0, 0.0), (274880.0, 0.0), (269893.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(240224.0, 0.0), (244346.0, 0.0), (244626.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'numa_memory': '112G', 'threads': 28, 'db': 'kvdb-st', 'bench': 'tpcc'}, [(220897.0, 0.0), (224565.0, 0.0), (224131.0, 0.0)]), ({'par_load': False, 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(200114.0, 2302.91), (199003.0, 2297.07), (200227.0, 2319.16)]), ({'par_load': False, 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(194843.0, 2323.13), (186419.0, 2159.79), (194520.0, 2306.21)]), ({'par_load': False, 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(192514.0, 2365.37), (193027.0, 2379.28), (193225.0, 2340.44)]), ({'par_load': False, 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(191380.0, 2385.91), (191342.0, 2390.18), (190192.0, 2357.07)]), ({'par_load': False, 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(191257.0, 2400.22), (180790.0, 2180.2), (190691.0, 2383.46)]), ({'par_load': False, 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(189927.0, 2360.62), (180441.0, 2180.87), (190903.0, 2394.66)]), ({'par_load': False, 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(162328.0, 14288.5), (165077.0, 14635.6), (165270.0, 14658.5)]), ({'par_load': False, 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(158432.0, 15909.9), (158501.0, 15990.8), (156767.0, 15640.1)]), ({'par_load': False, 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(153417.0, 17102.2), (154920.0, 17297.1), (153161.0, 16958.5)]), ({'par_load': False, 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(150880.0, 18313.4), (143189.0, 17586.6), (152049.0, 18577.0)]), ({'par_load': False, 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(149056.0, 19671.2), (150192.0, 19849.1), (150495.0, 19815.7)]), ({'par_load': False, 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'numa_memory': '64G', 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(147858.0, 20842.0), (146716.0, 20584.7), (147927.0, 20903.4)])] + [({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, [(32240.8, 0.0), (31026.7, 0.0), (30438.4, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 4, 'db': 'ndb-proto1', 'bench': 'tpcc'}, [(112148.0, 4.91663), (113832.0, 5.29995), (113486.0, 5.19994)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 8, 'db': 'ndb-proto1', 'bench': 'tpcc'}, [(212410.0, 8.81654), (212020.0, 9.64986), (213780.0, 8.94986)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 12, 'db': 'ndb-proto1', 'bench': 'tpcc'}, [(320426.0, 13.3998), (327475.0, 14.4498), (321080.0, 14.6498)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 16, 'db': 'ndb-proto1', 'bench': 'tpcc'}, [(431773.0, 18.8163), (432635.0, 18.3997), (430719.0, 18.383)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 20, 'db': 'ndb-proto1', 'bench': 'tpcc'}, [(542721.0, 23.7831), (529936.0, 21.7331), (538936.0, 23.8329)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 24, 'db': 'ndb-proto1', 'bench': 'tpcc'}, [(642353.0, 28.483), (643512.0, 28.7329), (643497.0, 29.6495)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 28, 'db': 'ndb-proto1', 'bench': 'tpcc'}, [(743009.0, 32.7986), (729192.0, 32.7657), (728048.0, 31.4827)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '112G', 'threads': 32, 'db': 'ndb-proto1', 'bench': 'tpcc'}, [(790212.0, 34.2826), (805566.0, 35.166), (804736.0, 34.3825)])] + [({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '101G', 'threads': 1, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(614240.0, 0.0), (616696.0, 0.0), (616061.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '101G', 'threads': 1, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(521324.0, 0.0), (519604.0, 0.0), (516847.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '105G', 'threads': 4, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(2463990.0, 0.0), (2465270.0, 0.0), (2467840.0, 0.0166664)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '105G', 'threads': 4, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(2064040.0, 0.0), (2035160.0, 0.0), (2014690.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '111G', 'threads': 8, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(4632640.0, 0.0), (4628850.0, 0.0), (4642110.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '111G', 'threads': 8, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(3917340.0, 0.0), (3918600.0, 0.0166663), (3906720.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '116G', 'threads': 12, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(6392020.0, 0.0), (6261500.0, 0.0166661), (6373530.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '116G', 'threads': 12, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(5383630.0, 0.0), (5386750.0, 0.0), (5349580.0, 0.0166662)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '122G', 'threads': 16, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(8197750.0, 0.0), (8202670.0, 0.0166657), (8212610.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '122G', 'threads': 16, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(6964480.0, 0.0), (6962350.0, 0.0), (6964480.0, 0.0333319)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '128G', 'threads': 20, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(9569340.0, 0.0), (9545470.0, 0.0), (9525760.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '128G', 'threads': 20, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(8327020.0, 0.016665), (8351850.0, 0.0666599), (8327510.0, 0.0499968)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '133G', 'threads': 24, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(9695090.0, 0.0), (9715240.0, 0.0), (9725800.0, 0.0166652)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '133G', 'threads': 24, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(9238990.0, 0.0333301), (9226620.0, 0.0666602), (9170780.0, 0.0999898)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '139G', 'threads': 28, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(8925020.0, 0.0166638), (8880120.0, 0.0), (9184000.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '139G', 'threads': 28, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(9286140.0, 0.0666553), (8793640.0, 0.049994), (9385870.0, 0.11665)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,20,0,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale', 'numa_memory': '144G', 'threads': 32, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(5234880.0, 0.0), (8694640.0, 0.0), (8797910.0, 0.0)]), ({'par_load': True, 'bench_opts': '--workload-mix 80,0,20,0', 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'numa_memory': '144G', 'threads': 32, 'db': 'ndb-proto1', 'bench': 'ycsb'}, [(8860080.0, 0.0666507), (8879010.0, 0.0833041), (8945930.0, 0.0)])]
diff --git a/silo/benchmarks/results/istc11-8-28-13_cameraready.py b/silo/benchmarks/results/istc11-8-28-13_cameraready.py
new file mode 100644 (file)
index 0000000..e8cc6cd
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(38179.9, 38179.9, 0.0261386, 0.0, 0.0), (38455.4, 38455.4, 0.0259527, 0.0, 0.0), (38349.3, 38349.3, 0.0260178, 0.0, 0.0)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(27279.2, 27279.2, 0.0365973, 0.0, 0.0), (27155.6, 27155.6, 0.0367648, 0.0, 0.0), (26796.8, 26796.8, 0.037253, 0.0, 0.0)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(26321.0, 26321.0, 0.0379286, 0.0, 0.0), (26569.2, 26569.2, 0.037573, 0.0, 0.0), (27140.4, 27140.4, 0.0367824, 0.0, 0.0)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(26820.0, 26820.0, 0.0372239, 0.0, 0.0), (26198.6, 26198.6, 0.0381046, 0.0, 0.0), (26721.0, 26721.0, 0.0373651, 0.0, 0.0)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(27636.2, 27636.2, 0.0361241, 0.0, 0.0), (27896.6, 27896.6, 0.0357849, 0.0, 0.0), (27195.4, 27195.4, 0.0367132, 0.0, 0.0)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(53665.2, 53665.2, 0.0371825, 0.0, 2.33332), (55335.5, 55335.5, 0.0360387, 0.0, 2.99998), (55456.1, 55456.1, 0.0359656, 0.0, 2.44999)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(54570.7, 54570.7, 0.0365389, 0.0, 2.76665), (55027.4, 55027.4, 0.0362424, 0.0, 2.64998), (52970.9, 52970.9, 0.0376617, 0.0, 2.29999)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(54740.4, 54740.4, 0.0364358, 0.0, 2.51665), (53974.4, 53974.4, 0.0369523, 0.0, 2.79998), (53956.9, 53956.9, 0.0369649, 0.0, 2.66665)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(55583.4, 55583.4, 0.0358752, 0.0, 2.74998), (55511.9, 55511.9, 0.0359357, 0.0, 2.81665), (55076.5, 55076.5, 0.0362123, 0.0, 2.83332)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(102115.0, 102115.0, 0.0389772, 0.0, 14.9832), (102483.0, 102483.0, 0.0388438, 0.0, 15.1499), (99394.2, 99394.2, 0.0400376, 0.0, 15.4666)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(99742.6, 99742.6, 0.0399078, 0.0, 14.7833), (99686.9, 99686.9, 0.0399313, 0.0, 15.0999), (98806.6, 98806.6, 0.0402783, 0.0, 15.0666)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(101562.0, 101562.0, 0.0391926, 0.0, 14.7999), (101148.0, 101148.0, 0.03935, 0.0, 14.8499), (100588.0, 100588.0, 0.0395722, 0.0, 14.3666)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(101855.0, 101855.0, 0.0390631, 0.0, 16.1166), (103506.0, 103506.0, 0.0384577, 0.0, 15.6666), (104612.0, 104612.0, 0.0380519, 0.0, 14.6166)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(105894.0, 105894.0, 0.0402846, 0.0, 1781.74), (106271.0, 106271.0, 0.0401367, 0.0, 1796.52), (103814.0, 103814.0, 0.0411498, 0.0, 1772.05)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(103324.0, 103324.0, 0.0413357, 0.0, 1784.36), (107888.0, 107888.0, 0.0394036, 0.0, 1793.97), (105044.0, 105044.0, 0.0406493, 0.0, 1787.03)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(104675.0, 104675.0, 0.0407454, 0.0, 1819.53), (103649.0, 103649.0, 0.0411788, 0.0, 1792.34), (105401.0, 105401.0, 0.0404608, 0.0, 1828.0)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(118134.0, 118134.0, 0.0462966, 0.0, 459.179), (116671.0, 116671.0, 0.0467614, 0.0, 451.031), (120292.0, 120292.0, 0.0458435, 0.0, 503.747)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(110164.0, 110164.0, 0.0415078, 0.0, 3593.76), (109447.0, 109447.0, 0.0417149, 0.0, 3590.73), (109431.0, 109431.0, 0.0418349, 0.0, 3584.31)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(107558.0, 107558.0, 0.0427503, 0.0, 3538.26), (108211.0, 108211.0, 0.042309, 0.0, 3571.77), (108623.0, 108623.0, 0.0421955, 0.0, 3573.84)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(103702.0, 103702.0, 0.0444855, 0.0, 3527.93), (107269.0, 107269.0, 0.0427647, 0.0, 3571.43), (110737.0, 110737.0, 0.041177, 0.0, 3588.79)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(142348.0, 142348.0, 0.0479053, 0.0, 939.884), (137373.0, 137373.0, 0.0501871, 0.0, 917.892), (138177.0, 138177.0, 0.0498929, 0.0, 916.069)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(109780.0, 109780.0, 0.0442741, 0.0, 5086.42), (107663.0, 107663.0, 0.0452148, 0.0, 5028.67), (108948.0, 108948.0, 0.0446894, 0.0, 5072.1)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(109759.0, 109759.0, 0.0442411, 0.0, 5083.12), (108137.0, 108137.0, 0.0450479, 0.0, 5057.78), (108815.0, 108815.0, 0.04471, 0.0, 5038.11)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(106691.0, 106691.0, 0.0458682, 0.0, 5043.55), (108021.0, 108021.0, 0.0450373, 0.0, 5041.45), (110638.0, 110638.0, 0.0437823, 0.0, 5068.65)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(129149.0, 129149.0, 0.0540999, 0.0, 1050.48), (109476.0, 109476.0, 0.0503589, 0.0, 924.406), (142261.0, 142261.0, 0.0537856, 0.0, 1222.93)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(112102.0, 112102.0, 0.045725, 0.0, 6523.81), (112269.0, 112269.0, 0.0455701, 0.0, 6531.66), (111602.0, 111602.0, 0.0459453, 0.0, 6530.73)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(109293.0, 109293.0, 0.0469943, 0.0, 6478.34), (111680.0, 111680.0, 0.0460389, 0.0, 6533.16), (110390.0, 110390.0, 0.0465508, 0.0, 6495.78)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(107479.0, 107479.0, 0.0480774, 0.0, 6462.35), (111586.0, 111586.0, 0.0459079, 0.0, 6531.83), (110096.0, 110096.0, 0.0466755, 0.0, 6489.14)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(151740.0, 151740.0, 0.056124, 0.0, 1667.71), (102389.0, 102389.0, 0.0534389, 0.0, 1073.26), (147175.0, 147175.0, 0.0557127, 0.0, 1616.25)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(112078.0, 112078.0, 0.0503686, 0.0, 9258.67), (110439.0, 110439.0, 0.0512938, 0.0, 9228.7), (112589.0, 112589.0, 0.0498881, 0.0, 9275.0)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(110793.0, 110793.0, 0.0509917, 0.0, 9266.3), (111479.0, 111479.0, 0.050596, 0.0, 9230.4), (113947.0, 113947.0, 0.0492867, 0.0, 9307.76)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(114086.0, 114086.0, 0.0490479, 0.0, 9354.82), (112389.0, 112389.0, 0.0500887, 0.0, 9263.79), (112203.0, 112203.0, 0.0501601, 0.0, 9268.97)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(96140.3, 96140.3, 0.0602456, 0.0, 1317.04), (123833.0, 123833.0, 0.0586249, 0.0, 1612.86), (91066.7, 91066.7, 0.0622132, 0.0, 1227.0)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(113260.0, 113260.0, 0.053017, 0.0, 11754.7), (112459.0, 112459.0, 0.053476, 0.0, 11714.3), (112940.0, 112940.0, 0.0533785, 0.0, 11758.2)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(112123.0, 112123.0, 0.0534119, 0.0, 11666.5), (115343.0, 115343.0, 0.0518274, 0.0, 11819.3), (110106.0, 110106.0, 0.0551375, 0.0, 11655.7)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(111807.0, 111807.0, 0.0539485, 0.0, 11719.9), (112420.0, 112420.0, 0.0538304, 0.0, 11787.5), (113152.0, 113152.0, 0.052745, 0.0, 11688.3)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(129850.0, 129850.0, 0.0655325, 0.0, 1906.86), (113485.0, 113485.0, 0.0637675, 0.0, 1613.24), (129069.0, 129069.0, 0.0640064, 0.0, 1774.91)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(112045.0, 112045.0, 0.0562702, 0.0, 13864.4), (112644.0, 112644.0, 0.0561196, 0.0, 13896.3), (109792.0, 109792.0, 0.0558557, 0.0, 13456.0)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(112941.0, 112941.0, 0.0558932, 0.0, 13915.8), (113446.0, 113446.0, 0.0542462, 0.0, 13645.3), (112769.0, 112769.0, 0.0559335, 0.0, 13895.2)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(112793.0, 112793.0, 0.0559056, 0.0, 13849.6), (112508.0, 112508.0, 0.0558038, 0.0, 13815.1), (111499.0, 111499.0, 0.0569646, 0.0, 13887.4)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(120387.0, 120387.0, 0.064223, 0.0, 1863.88), (92974.0, 92974.0, 0.0662328, 0.0, 1458.06), (142175.0, 142175.0, 0.0656398, 0.0, 2178.4)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(111017.0, 111017.0, 0.0584187, 0.0, 15620.5), (115832.0, 115832.0, 0.056032, 0.0, 16005.3), (109617.0, 109617.0, 0.0594124, 0.0, 15484.4)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(96515.9, 96515.9, 0.0568733, 0.0, 13313.0), (111291.0, 111291.0, 0.057934, 0.0, 15575.5), (112729.0, 112729.0, 0.0574871, 0.0, 15752.4)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(111314.0, 111314.0, 0.0587607, 0.0, 15708.9), (102549.0, 102549.0, 0.0573524, 0.0, 14308.9), (112734.0, 112734.0, 0.0576216, 0.0, 15792.8)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(125184.0, 125184.0, 0.0663509, 0.0, 1955.04), (94220.5, 94220.5, 0.0677441, 0.0, 1598.4), (105873.0, 105873.0, 0.0695081, 0.0, 1697.33)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(93351.6, 93351.6, 0.060823, 0.0, 13923.2), (107609.0, 107609.0, 0.0615199, 0.0, 16512.1), (102195.0, 102195.0, 0.0653834, 0.0, 15340.9)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(103391.0, 103391.0, 0.0650066, 0.0, 15606.9), (90638.3, 90638.3, 0.0607784, 0.0, 13671.9), (106724.0, 106724.0, 0.0613764, 0.0, 16335.3)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(106892.0, 106892.0, 0.0621241, 0.0, 16275.5), (102397.0, 102397.0, 0.0621106, 0.0, 15317.1), (107323.0, 107323.0, 0.0606884, 0.0, 16274.8)]), ({'binary': '../out-backoff/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(95339.6, 95339.6, 0.0722419, 0.0, 1606.52), (132200.0, 132200.0, 0.074819, 0.0, 2214.72), (89102.4, 89102.4, 0.0709545, 0.0, 1390.29)])]
diff --git a/silo/benchmarks/results/istc12-8-30-13_cameraready.py b/silo/benchmarks/results/istc12-8-30-13_cameraready.py
new file mode 100644 (file)
index 0000000..e3348b1
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-factor-gc-nowriteinplace/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10 --disable-read-only-snapshots', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(398905.0, 398905.0, 0.0700164, 0.0, 69.9142), (402620.0, 402620.0, 0.069386, 0.0, 70.782), (401382.0, 401382.0, 0.0695872, 0.0, 71.7488)]), ({'binary': '../out-factor-gc-nowriteinplace/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10 --disable-read-only-snapshots', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(453830.0, 453830.0, 0.0615368, 0.0, 86.2485), (451226.0, 451226.0, 0.0618907, 0.0, 84.082), (450126.0, 450126.0, 0.0620415, 0.0, 85.2653)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10 --disable-read-only-snapshots', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(596865.0, 596865.0, 0.0467633, 0.0, 110.447), (604516.0, 604516.0, 0.0461746, 0.0, 113.597), (600628.0, 600628.0, 0.0464617, 0.0, 112.809)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(566476.0, 566476.0, 0.0493026, 0.0, 15.8496), (563466.0, 563466.0, 0.0495645, 0.0, 15.9314), (566009.0, 566009.0, 0.0493417, 0.0, 15.7497)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': True, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(589524.0, 589524.0, 0.0473696, 0.0, 16.4164), (596908.0, 596908.0, 0.0467816, 0.0, 17.8163), (591850.0, 591850.0, 0.047182, 0.0, 17.0831)])]
diff --git a/silo/benchmarks/results/istc3-10-23-13.py b/silo/benchmarks/results/istc3-10-23-13.py
new file mode 100644 (file)
index 0000000..ed9bdee
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(665194.0, 665194.0, 0.00145799, 0.0, 0.0), (664321.0, 664321.0, 0.00145999, 0.0, 0.0), (659362.0, 659362.0, 0.00147133, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(627665.0, 627665.0, 0.00154717, 0.0, 0.0), (638707.0, 638707.0, 0.00151958, 0.0, 0.0), (630758.0, 630758.0, 0.00153936, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(632883.0, 632883.0, 0.00153415, 0.0, 0.0), (633225.0, 633225.0, 0.00153232, 0.0, 0.0), (616378.0, 616378.0, 0.00157641, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(2672640.0, 2672640.0, 0.00144922, 0.0, 0.0), (2675660.0, 2675660.0, 0.00144759, 0.0, 0.0), (2673970.0, 2673970.0, 0.00144849, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(2527320.0, 2527320.0, 0.00153448, 0.0, 0.0166666), (2513740.0, 2513740.0, 0.0015435, 0.0, 0.0), (2517120.0, 2517120.0, 0.00154078, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(2524500.0, 2524500.0, 0.00153619, 0.0, 0.0), (2517640.0, 2517640.0, 0.00154114, 0.0, 0.0), (2525360.0, 2525360.0, 0.0015354, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(5064130.0, 5064130.0, 0.00153162, 0.0, 0.0), (5062450.0, 5062450.0, 0.00153251, 0.0, 0.0), (5073880.0, 5073880.0, 0.00152869, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(4745030.0, 4745030.0, 0.00163794, 0.0, 0.0), (4746190.0, 4746190.0, 0.00163751, 0.0, 0.033333), (4758670.0, 4758670.0, 0.00163252, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(4743920.0, 4743920.0, 0.00163778, 0.0, 0.033333), (4782210.0, 4782210.0, 0.00162426, 0.0, 0.0333331), (4760970.0, 4760970.0, 0.00163172, 0.0, 0.0166665)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(7086380.0, 7086380.0, 0.00164577, 0.0, 0.0), (7042810.0, 7042810.0, 0.00165601, 0.0, 0.0), (7082990.0, 7082990.0, 0.00164658, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(6686180.0, 6686180.0, 0.00174688, 0.0, 0.0166665), (6692920.0, 6692920.0, 0.00174363, 0.0, 0.0), (6713280.0, 6713280.0, 0.00173967, 0.0, 0.0166665)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(6762270.0, 6762270.0, 0.00172609, 0.0, 0.0), (6782020.0, 6782020.0, 0.00172096, 0.0, 0.0), (6786750.0, 6786750.0, 0.00172036, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(9181960.0, 9181960.0, 0.0016948, 0.0, 0.0), (9168390.0, 9168390.0, 0.00169683, 0.0, 0.0), (9171090.0, 9171090.0, 0.00169678, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(8634840.0, 8634840.0, 0.00180441, 0.0, 0.033333), (8622440.0, 8622440.0, 0.0018062, 0.0, 0.0), (8564850.0, 8564850.0, 0.0018196, 0.0, 0.0166665)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(8795810.0, 8795810.0, 0.00176914, 0.0, 0.033333), (8803920.0, 8803920.0, 0.00176931, 0.0, 0.0), (8799200.0, 8799200.0, 0.00176973, 0.0, 0.0333329)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(11387900.0, 11387900.0, 0.00170838, 0.0, 0.0), (11496800.0, 11496800.0, 0.00169184, 0.0, 0.0), (11488500.0, 11488500.0, 0.00169307, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(9738990.0, 9738990.0, 0.00200578, 0.0, 0.0333329), (9760700.0, 9760700.0, 0.00200063, 0.0, 0.0666657), (9651980.0, 9651980.0, 0.0020237, 0.0, 0.0166665)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(10726600.0, 10726600.0, 0.00181605, 0.0, 0.0333329), (10946000.0, 10946000.0, 0.00177867, 0.0, 0.0166665), (11034900.0, 11034900.0, 0.00176453, 0.0, 0.0166664)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(13516700.0, 13516700.0, 0.00172772, 0.0, 0.0), (13501900.0, 13501900.0, 0.00172956, 0.0, 0.0), (13495600.0, 13495600.0, 0.00173061, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(9087760.0, 9087760.0, 0.00259311, 0.0, 0.0166664), (9163000.0, 9163000.0, 0.00257069, 0.0, 0.0), (9060470.0, 9060470.0, 0.00260104, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(12933300.0, 12933300.0, 0.00180766, 0.0, 0.0), (12992900.0, 12992900.0, 0.00179912, 0.0, 0.0666659), (12976100.0, 12976100.0, 0.00180094, 0.0, 0.0166663)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(15893600.0, 15893600.0, 0.00171395, 0.0, 0.0), (15900000.0, 15900000.0, 0.00171336, 0.0, 0.0), (15939600.0, 15939600.0, 0.0017089, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(8755330.0, 8755330.0, 0.00315022, 0.0, 0.0), (8801710.0, 8801710.0, 0.0031334, 0.0, 0.0), (8809090.0, 8809090.0, 0.00312979, 0.0, 0.0166662)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(15263600.0, 15263600.0, 0.00178589, 0.0, 0.0666651), (15260000.0, 15260000.0, 0.00178694, 0.0, 0.0333326), (15193000.0, 15193000.0, 0.00179443, 0.0, 0.0499989)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(17624800.0, 17624800.0, 0.00176737, 0.0, 0.0), (17596700.0, 17596700.0, 0.00177021, 0.0, 0.0), (17591600.0, 17591600.0, 0.00177088, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(8651780.0, 8651780.0, 0.00364947, 0.0, 0.0166665), (8609210.0, 8609210.0, 0.00366802, 0.0, 0.0499984), (8626100.0, 8626100.0, 0.00366151, 0.0, 0.0166658)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(16785800.0, 16785800.0, 0.00185811, 0.0, 0.0833316), (16885600.0, 16885600.0, 0.00184669, 0.0, 0.0999984), (16856700.0, 16856700.0, 0.0018493, 0.0, 0.0499987)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(33646.5, 33646.5, 0.029612, 80.1205, 0.0), (34026.1, 34026.1, 0.0292766, 84.0236, 0.0), (33634.0, 33634.0, 0.0296285, 81.7065, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(33999.7, 33999.7, 0.0292969, 79.9259, 0.0), (33163.8, 33163.8, 0.0300449, 79.375, 0.0), (33467.8, 33467.8, 0.0297745, 80.003, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(36249.2, 36249.2, 0.0275061, 0.0, 0.0), (36970.8, 36970.8, 0.0269678, 0.0, 0.0), (36277.1, 36277.1, 0.0274837, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(121583.0, 121583.0, 0.0327675, 106.508, 4.52726), (121542.0, 121542.0, 0.0327998, 94.6702, 4.01465), (121066.0, 121066.0, 0.0329226, 96.6235, 4.6125)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(124093.0, 124093.0, 0.0321134, 94.2461, 4.76184), (123089.0, 123089.0, 0.032393, 90.4293, 4.79816), (122826.0, 122826.0, 0.0324568, 92.4991, 4.92956)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(130342.0, 130342.0, 0.0306012, 0.0, 5.96663), (134389.0, 134389.0, 0.0296727, 0.0, 5.03329), (133669.0, 133669.0, 0.0298368, 0.0, 5.59994)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(233347.0, 233347.0, 0.0341563, 140.331, 9.35607), (232957.0, 232957.0, 0.0342152, 176.826, 8.90821), (231095.0, 231095.0, 0.0345125, 154.034, 8.71245)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(230754.0, 230754.0, 0.0345627, 101.877, 9.39432), (228740.0, 228740.0, 0.0348503, 95.3754, 9.69039), (230134.0, 230134.0, 0.034648, 106.236, 9.50989)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(249296.0, 249296.0, 0.0320019, 0.0, 10.1333), (251357.0, 251357.0, 0.0317386, 0.0, 11.1499), (252833.0, 252833.0, 0.0315527, 0.0, 10.5499)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(345270.0, 345270.0, 0.0346358, 181.773, 12.6565), (347677.0, 347677.0, 0.0343883, 225.347, 13.6364), (349664.0, 349664.0, 0.0341994, 163.489, 13.8387)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(344102.0, 344102.0, 0.0347278, 110.551, 13.299), (349593.0, 349593.0, 0.0342056, 96.7936, 13.938), (350814.0, 350814.0, 0.0340899, 96.8305, 13.6586)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(372815.0, 372815.0, 0.0320968, 0.0, 15.6164), (376173.0, 376173.0, 0.0318084, 0.0, 15.8997), (371302.0, 371302.0, 0.0322255, 0.0, 15.8997)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(455586.0, 455586.0, 0.034978, 278.733, 17.9587), (452241.0, 452241.0, 0.0351256, 255.779, 17.7525), (454454.0, 454454.0, 0.0350775, 267.553, 17.7474)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(453779.0, 453779.0, 0.0351553, 99.9213, 18.1767), (455583.0, 455583.0, 0.0350079, 103.563, 17.54), (454717.0, 454717.0, 0.0350833, 99.4235, 18.2768)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(488973.0, 488973.0, 0.0326302, 0.0, 21.2665), (490321.0, 490321.0, 0.0325391, 0.0, 22.3792), (497241.0, 497241.0, 0.0320882, 0.0, 21.0299)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(567455.0, 567455.0, 0.0348778, 257.245, 21.2503), (562582.0, 562582.0, 0.0353522, 400.975, 22.9508), (566166.0, 566166.0, 0.0352049, 300.27, 22.463)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(577258.0, 577258.0, 0.0345186, 100.998, 22.4772), (569533.0, 569533.0, 0.0349996, 99.6071, 21.4014), (573197.0, 573197.0, 0.0347675, 101.362, 22.0443)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(623848.0, 623848.0, 0.031967, 0.0, 26.345), (621572.0, 621572.0, 0.0320872, 0.0, 26.4496), (625838.0, 625838.0, 0.0318558, 0.0, 25.4792)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(666369.0, 666369.0, 0.0354124, 335.548, 25.9776), (675336.0, 675336.0, 0.0353886, 244.572, 27.0097), (665293.0, 665293.0, 0.0359331, 408.702, 25.9481)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(674851.0, 674851.0, 0.0354423, 102.418, 26.6617), (683970.0, 683970.0, 0.034972, 100.681, 26.3128), (682250.0, 682250.0, 0.0350574, 100.38, 27.5601)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(740028.0, 740028.0, 0.0323347, 0.0, 30.3467), (741882.0, 741882.0, 0.0322591, 0.0, 30.6996), (740927.0, 740927.0, 0.0323014, 0.0, 31.5828)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(755412.0, 755412.0, 0.0363388, 968.997, 29.208), (749625.0, 749625.0, 0.0361973, 1614.41, 29.1154), (752208.0, 752208.0, 0.0361689, 1596.16, 28.6564)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(779616.0, 779616.0, 0.0357905, 104.391, 30.8379), (779070.0, 779070.0, 0.0358176, 104.903, 29.8733), (786200.0, 786200.0, 0.0354812, 105.349, 31.0659)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(864650.0, 864650.0, 0.0322888, 0.0, 34.4092), (859099.0, 859099.0, 0.0325006, 0.0, 35.8661), (858676.0, 858676.0, 0.0325163, 0.0, 35.9827)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(794606.0, 794606.0, 0.0381952, 8686.11, 30.5177), (792937.0, 792937.0, 0.0383354, 5444.97, 30.1904), (766209.0, 766209.0, 0.0382494, 17857.9, 28.0529)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(841285.0, 841285.0, 0.0378647, 340.501, 33.3706), (872640.0, 872640.0, 0.036514, 466.928, 33.3543), (871260.0, 871260.0, 0.0365663, 527.723, 33.5256)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(991642.0, 991642.0, 0.0321701, 0.0, 41.6824), (994802.0, 994802.0, 0.0320704, 0.0, 39.5654), (993671.0, 993671.0, 0.0321026, 0.0, 39.7486)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1033030.0, 1033030.0, 0.0270392, 0.0, 0.0), (1033550.0, 1033550.0, 0.0270258, 0.0, 0.0), (1036600.0, 1036600.0, 0.0269456, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1021410.0, 1021410.0, 0.0273416, 0.0, 147.513), (1021190.0, 1021190.0, 0.0273543, 0.0, 149.014), (1008930.0, 1008930.0, 0.0276875, 0.0, 148.564)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(981914.0, 981914.0, 0.028443, 0.0, 286.642), (1012990.0, 1012990.0, 0.0275678, 0.0, 299.629), (1003260.0, 1003260.0, 0.0278304, 0.0, 293.704)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1001810.0, 1001810.0, 0.0278721, 0.0, 435.229), (992926.0, 992926.0, 0.0281152, 0.0, 428.495), (1005500.0, 1005500.0, 0.0277683, 0.0, 438.603)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(997260.0, 997260.0, 0.0279922, 0.0, 582.686), (1001110.0, 1001110.0, 0.0278849, 0.0, 584.443), (983651.0, 983651.0, 0.028374, 0.0, 570.248)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(988359.0, 988359.0, 0.0282336, 0.0, 711.884), (992980.0, 992980.0, 0.0281087, 0.0, 715.669), (979124.0, 979124.0, 0.0285054, 0.0, 714.291)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(981990.0, 981990.0, 0.0284105, 0.0, 840.281), (979172.0, 979172.0, 0.0284929, 0.0, 835.256), (990452.0, 990452.0, 0.0281752, 0.0, 855.436)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(980409.0, 980409.0, 0.0284507, 0.0, 975.658), (958352.0, 958352.0, 0.0291135, 0.0, 961.153), (958207.0, 958207.0, 0.0291182, 0.0, 963.485)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(972633.0, 972633.0, 0.0286723, 0.0, 1099.53), (982107.0, 982107.0, 0.0284022, 0.0, 1123.94), (952070.0, 952070.0, 0.0293025, 0.0, 1086.55)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(978183.0, 978183.0, 0.0285034, 0.0, 1238.53), (977567.0, 977567.0, 0.0285293, 0.0, 1254.63), (977362.0, 977362.0, 0.0285355, 0.0, 1242.97)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(973667.0, 973667.0, 0.0286368, 0.0, 1368.57), (977250.0, 977250.0, 0.0285327, 0.0, 1388.81), (971705.0, 971705.0, 0.0286909, 0.0, 1359.39)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1075290.0, 1075290.0, 0.0259774, 0.0, 0.0), (1078920.0, 1078920.0, 0.0258894, 0.0, 0.0), (1071830.0, 1071830.0, 0.026064, 0.0, 0.0)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1053780.0, 1053780.0, 0.0265098, 0.0, 158.632), (1061900.0, 1061900.0, 0.0263005, 0.0, 158.312), (1037270.0, 1037270.0, 0.02694, 0.0, 155.341)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1056410.0, 1056410.0, 0.0264268, 0.0, 315.825), (1060370.0, 1060370.0, 0.0263327, 0.0, 316.946), (1060400.0, 1060400.0, 0.0263329, 0.0, 312.18)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1013930.0, 1013930.0, 0.0275357, 0.0, 449.123), (1047410.0, 1047410.0, 0.0266544, 0.0, 459.988), (1054260.0, 1054260.0, 0.0264945, 0.0, 463.285)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1048140.0, 1048140.0, 0.0266425, 0.0, 609.624), (1013100.0, 1013100.0, 0.0275677, 0.0, 600.51), (1040210.0, 1040210.0, 0.026832, 0.0, 609.559)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1024670.0, 1024670.0, 0.0272472, 0.0, 740.583), (1019610.0, 1019610.0, 0.0273839, 0.0, 754.886), (1025300.0, 1025300.0, 0.027226, 0.0, 747.172)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1004510.0, 1004510.0, 0.0277897, 0.0, 884.956), (1031020.0, 1031020.0, 0.0270751, 0.0, 908.855), (1018950.0, 1018950.0, 0.0273968, 0.0, 888.021)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1008650.0, 1008650.0, 0.027665, 0.0, 1022.02), (1033230.0, 1033230.0, 0.0269973, 0.0, 1048.82), (1038160.0, 1038160.0, 0.0268679, 0.0, 1057.27)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1030810.0, 1030810.0, 0.0270679, 0.0, 1187.97), (999149.0, 999149.0, 0.0279264, 0.0, 1152.75), (1019760.0, 1019760.0, 0.0273473, 0.0, 1174.73)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1013880.0, 1013880.0, 0.0275101, 0.0, 1313.72), (1014470.0, 1014470.0, 0.0275005, 0.0, 1310.09), (983344.0, 983344.0, 0.0283589, 0.0, 1269.04)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1021650.0, 1021650.0, 0.0272973, 0.0, 1458.36), (1001840.0, 1001840.0, 0.0278367, 0.0, 1429.79), (1022540.0, 1022540.0, 0.0272602, 0.0, 1451.32)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1163900.0, 1163900.0, 0.0239978, 0.0, 0.0), (1148930.0, 1148930.0, 0.0243111, 0.0, 0.0), (1149080.0, 1149080.0, 0.0243212, 0.0, 0.0)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1112200.0, 1112200.0, 0.0251234, 0.0, 164.363), (1139350.0, 1139350.0, 0.0245084, 0.0, 169.774), (1148990.0, 1148990.0, 0.0243014, 0.0, 169.225)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1132420.0, 1132420.0, 0.0246486, 0.0, 334.76), (1109160.0, 1109160.0, 0.0251866, 0.0, 327.961), (1108940.0, 1108940.0, 0.0251917, 0.0, 328.695)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1128540.0, 1128540.0, 0.024732, 0.0, 499.074), (1117360.0, 1117360.0, 0.0249961, 0.0, 496.675), (1095090.0, 1095090.0, 0.0255044, 0.0, 488.786)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1121500.0, 1121500.0, 0.0248837, 0.0, 660.428), (1106540.0, 1106540.0, 0.0252348, 0.0, 651.928), (1087730.0, 1087730.0, 0.0256723, 0.0, 642.205)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1100670.0, 1100670.0, 0.0253634, 0.0, 813.24), (1101470.0, 1101470.0, 0.0253323, 0.0, 802.889), (1094350.0, 1094350.0, 0.0255111, 0.0, 802.264)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1106510.0, 1106510.0, 0.0252107, 0.0, 966.833), (1091550.0, 1091550.0, 0.0255719, 0.0, 952.553), (1104750.0, 1104750.0, 0.0252476, 0.0, 968.01)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1088350.0, 1088350.0, 0.025641, 0.0, 1110.1), (1091850.0, 1091850.0, 0.0255452, 0.0, 1102.11), (1097620.0, 1097620.0, 0.025409, 0.0, 1120.19)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1072080.0, 1072080.0, 0.0260266, 0.0, 1237.24), (1088360.0, 1088360.0, 0.0256359, 0.0, 1255.33), (1089940.0, 1089940.0, 0.0255857, 0.0, 1259.93)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1085940.0, 1085940.0, 0.0256873, 0.0, 1410.5), (1075360.0, 1075360.0, 0.025935, 0.0, 1390.09), (1097310.0, 1097310.0, 0.0254081, 0.0, 1425.44)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1081110.0, 1081110.0, 0.025798, 0.0, 1554.79), (1090140.0, 1090140.0, 0.0255701, 0.0, 1552.01), (1064400.0, 1064400.0, 0.0261977, 0.0, 1516.22)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1533810.0, 1533810.0, 0.0182077, 0.0, 0.0), (1521750.0, 1521750.0, 0.0183467, 0.0, 0.0), (1520430.0, 1520430.0, 0.0183701, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1258850.0, 1258850.0, 0.0221954, 0.0, 0.0), (1263730.0, 1263730.0, 0.0221042, 0.0, 0.0), (1218520.0, 1218520.0, 0.0229307, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1014680.0, 1014680.0, 0.0275423, 0.0, 0.0), (1010200.0, 1010200.0, 0.0276688, 0.0, 0.0), (1030290.0, 1030290.0, 0.0271247, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(831370.0, 831370.0, 0.0336317, 0.0, 0.0), (853830.0, 853830.0, 0.0327386, 0.0, 0.0), (850073.0, 850073.0, 0.0328919, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(694986.0, 694986.0, 0.0402413, 0.0, 0.0), (679188.0, 679188.0, 0.0411693, 0.0, 0.0), (690933.0, 690933.0, 0.0404774, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(579706.0, 579706.0, 0.048252, 0.0, 0.0), (577753.0, 577753.0, 0.0484129, 0.0, 0.0), (588444.0, 588444.0, 0.0475357, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(479047.0, 479047.0, 0.0583913, 0.0, 0.0), (493020.0, 493020.0, 0.0567452, 0.0, 0.0), (487196.0, 487196.0, 0.0574239, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(427406.0, 427406.0, 0.0654586, 0.0, 0.0), (417453.0, 417453.0, 0.0670204, 0.0, 0.0), (433210.0, 433210.0, 0.0645865, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(378740.0, 378740.0, 0.0738743, 0.0, 0.0), (374003.0, 374003.0, 0.0748143, 0.0, 0.0), (378535.0, 378535.0, 0.0739133, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(327063.0, 327063.0, 0.0855549, 0.0, 0.0), (337879.0, 337879.0, 0.0828181, 0.0, 0.0), (332812.0, 332812.0, 0.0840759, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(303470.0, 303470.0, 0.0922156, 0.0, 0.0), (299072.0, 299072.0, 0.0935658, 0.0, 0.0), (301654.0, 301654.0, 0.0927694, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(53892.3, 53892.3, 0.0185116, 0.0, 0.0), (54073.5, 54073.5, 0.0184473, 0.0, 0.0), (54042.1, 54042.1, 0.0184532, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(40341.0, 40341.0, 0.0247351, 0.0, 0.0), (40044.9, 40044.9, 0.0249105, 0.0, 0.0), (40133.2, 40133.2, 0.0248571, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(40750.0, 40750.0, 0.0244782, 0.0, 0.0), (40465.1, 40465.1, 0.0246588, 0.0, 0.0), (40489.3, 40489.3, 0.0246355, 0.0, 0.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': '8G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(78726.1, 78726.1, 0.0253487, 0.0, 4.03331), (77342.3, 77342.3, 0.0257943, 0.0, 4.01664), (79023.8, 79023.8, 0.0252515, 0.0, 3.83331)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': '8G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(80741.4, 80741.4, 0.0247134, 0.0, 4.0833), (81440.0, 81440.0, 0.0244944, 0.0, 4.04997), (81540.6, 81540.6, 0.0244643, 0.0, 4.13331)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(153914.0, 153914.0, 0.0259219, 0.0, 22.6998), (152730.0, 152730.0, 0.0261246, 0.0, 22.5165), (151860.0, 151860.0, 0.026269, 0.0, 21.8498)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(156155.0, 156155.0, 0.0255505, 0.0, 24.7998), (158082.0, 158082.0, 0.0252382, 0.0, 23.3498), (158245.0, 158245.0, 0.0252073, 0.0, 24.7498)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': '24G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(217577.0, 217577.0, 0.0255571, 0.0, 14879.1), (214895.0, 214895.0, 0.0259024, 0.0, 14679.5), (213137.0, 213137.0, 0.0261356, 0.0, 14392.7)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': '24G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(235037.0, 235037.0, 0.0252876, 0.0, 1217.14), (235615.0, 235615.0, 0.0252247, 0.0, 1216.66), (234097.0, 234097.0, 0.0253872, 0.0, 1210.04)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(279114.0, 279114.0, 0.0255675, 0.0, 29261.0), (275275.0, 275275.0, 0.0259244, 0.0, 28946.1), (275986.0, 275986.0, 0.0258717, 0.0, 28924.6)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(307719.0, 307719.0, 0.0256558, 0.0, 2363.34), (307989.0, 307989.0, 0.0256363, 0.0, 2359.81), (308379.0, 308379.0, 0.0255978, 0.0, 2367.41)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': '40G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(320095.0, 320095.0, 0.025741, 0.0, 56559.7), (321325.0, 321325.0, 0.0256287, 0.0, 56446.6), (320318.0, 320318.0, 0.0257409, 0.0, 56502.7)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': '40G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(382666.0, 382666.0, 0.0256374, 0.0, 4616.02), (378430.0, 378430.0, 0.0259206, 0.0, 4550.77), (383246.0, 383246.0, 0.025599, 0.0, 4613.83)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(356794.0, 356794.0, 0.0262698, 0.0, 81898.3), (354872.0, 354872.0, 0.0263922, 0.0, 82007.5), (355081.0, 355081.0, 0.0263984, 0.0, 81775.1)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(439016.0, 439016.0, 0.0267091, 0.0, 6607.12), (437660.0, 437660.0, 0.0267958, 0.0, 6612.46), (428831.0, 428831.0, 0.0273472, 0.0, 6474.98)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(409664.0, 409664.0, 0.0268756, 0.0, 145023.0), (407734.0, 407734.0, 0.0271094, 0.0, 143304.0), (410884.0, 410884.0, 0.0267275, 0.0, 145959.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(568960.0, 568960.0, 0.0272104, 0.0, 12840.8), (573271.0, 573271.0, 0.0270059, 0.0, 12901.6), (564518.0, 564518.0, 0.0274254, 0.0, 12756.5)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(414427.0, 414427.0, 0.0275793, 0.0, 199563.0), (417098.0, 417098.0, 0.0274794, 0.0, 199845.0), (414689.0, 414689.0, 0.02759, 0.0, 198661.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(687870.0, 687870.0, 0.0278629, 0.0, 20634.1), (683714.0, 683714.0, 0.0280316, 0.0, 20566.7), (680910.0, 680910.0, 0.0281516, 0.0, 20434.8)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(398938.0, 398938.0, 0.0290732, 0.0, 232084.0), (405759.0, 405759.0, 0.0279203, 0.0, 236090.0), (405847.0, 405847.0, 0.027864, 0.0, 236646.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(795489.0, 795489.0, 0.0286313, 0.0, 29819.7), (804561.0, 804561.0, 0.028297, 0.0, 30394.9), (804037.0, 804037.0, 0.0283039, 0.0, 30359.5)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(326437.0, 326437.0, 0.0283589, 0.0, 218991.0), (328130.0, 328130.0, 0.0282548, 0.0, 218805.0), (325996.0, 325996.0, 0.0285465, 0.0, 218760.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(907256.0, 907256.0, 0.028978, 0.0, 41208.1), (903835.0, 903835.0, 0.0291006, 0.0, 40772.9), (898291.0, 898291.0, 0.0292702, 0.0, 40843.9)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(286045.0, 286045.0, 0.0294122, 0.0, 202010.0), (285447.0, 285447.0, 0.0286748, 0.0, 199200.0), (286838.0, 286838.0, 0.0288154, 0.0, 202265.0)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(965868.0, 965868.0, 0.0303036, 0.0, 50604.5), (973013.0, 973013.0, 0.0301178, 0.0, 50643.1), (974751.0, 974751.0, 0.0300955, 0.0, 50867.0)]), ({'binary': '../out-factor-gc-nowriteinplace.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(539316.0, 539316.0, 0.0518045, 0.0, 28.2828), (540671.0, 540671.0, 0.0516787, 0.0, 26.8329), (542829.0, 542829.0, 0.0514702, 0.0, 28.2661)]), ({'binary': '../out-factor-gc-nowriteinplace.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(648180.0, 648180.0, 0.0430938, 0.0, 33.0161), (645082.0, 645082.0, 0.0433076, 0.0, 33.6994), (644681.0, 644681.0, 0.0433314, 0.0, 33.1494)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(861963.0, 861963.0, 0.0323939, 0.0, 35.8494), (863284.0, 863284.0, 0.0323445, 0.0, 36.0827), (860851.0, 860851.0, 0.0324312, 0.0, 35.1661)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--disable-read-only-snapshots', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(951699.0, 951699.0, 0.0293136, 0.0, 125.143), (931985.0, 931985.0, 0.0299406, 0.0, 121.942), (938140.0, 938140.0, 0.0297451, 0.0, 123.59)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--disable-read-only-snapshots', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': True, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(970053.0, 970053.0, 0.0287609, 0.0, 130.1), (968778.0, 968778.0, 0.0287986, 0.0, 129.871), (970676.0, 970676.0, 0.028742, 0.0, 131.372)]), ({'binary': '../out-factor-fake-compression.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(842443.0, 842443.0, 0.0331075, 97.3666, 35.4747), (843675.0, 843675.0, 0.0330794, 106.84, 35.1617), (846982.0, 846982.0, 0.0329294, 96.814, 34.1455)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': True, 'disable_madv_willneed': True}, [(721700.0, 721700.0, 0.0386586, 129.016, 25.3493), (718373.0, 718373.0, 0.0388376, 147.253, 25.8169), (724009.0, 724009.0, 0.0385333, 144.936, 25.484)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(255470.0, 255470.0, 0.0620555, 0.0, 3708.87), (254435.0, 254435.0, 0.0622975, 0.0, 3734.98), (257718.0, 257718.0, 0.0615015, 0.0, 3811.68)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(235383.0, 235383.0, 0.0673883, 0.0, 3421.55), (243195.0, 243195.0, 0.0651853, 0.0, 3660.02), (244964.0, 244964.0, 0.0647064, 0.0, 3729.13)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(245842.0, 245842.0, 0.0644727, 0.0, 3754.5), (243161.0, 243161.0, 0.0651775, 0.0, 3708.75), (245002.0, 245002.0, 0.0646901, 0.0, 3717.77)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(243715.0, 243715.0, 0.0650215, 0.0, 3805.53), (242694.0, 242694.0, 0.0652814, 0.0, 3839.5), (229482.0, 229482.0, 0.069083, 0.0, 3376.49)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(244305.0, 244305.0, 0.0648529, 0.0, 3873.37), (240537.0, 240537.0, 0.0658617, 0.0, 3800.32), (243395.0, 243395.0, 0.0651118, 0.0, 3797.1)]), ({'binary': '../out-perf.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(241649.0, 241649.0, 0.065564, 0.0, 3732.48), (242059.0, 242059.0, 0.065461, 0.0, 3789.36), (240808.0, 240808.0, 0.0658073, 0.0, 3731.09)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(204858.0, 204858.0, 0.0690839, 0.0, 19714.1), (205259.0, 205259.0, 0.0689556, 0.0, 19749.3), (205786.0, 205786.0, 0.0687412, 0.0, 19757.4)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(197188.0, 197188.0, 0.0705438, 0.0, 21442.2), (198972.0, 198972.0, 0.0699698, 0.0, 21664.0), (196096.0, 196096.0, 0.0709659, 0.0, 21213.4)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(196745.0, 196745.0, 0.0696571, 0.0, 23796.8), (194573.0, 194573.0, 0.070444, 0.0, 23573.4), (194135.0, 194135.0, 0.070552, 0.0, 23533.0)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(192016.0, 192016.0, 0.0703985, 0.0, 25279.3), (192908.0, 192908.0, 0.0700513, 0.0, 25443.5), (192155.0, 192155.0, 0.0702697, 0.0, 25437.7)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(187791.0, 187791.0, 0.0711075, 0.0, 26725.5), (187687.0, 187687.0, 0.0711262, 0.0, 26712.9), (187170.0, 187170.0, 0.0711972, 0.0, 26658.4)]), ({'binary': '../out-factor-gc.masstree/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(186841.0, 186841.0, 0.0704994, 0.0, 28420.0), (181821.0, 181821.0, 0.0724888, 0.0, 27485.4), (188836.0, 188836.0, 0.0697673, 0.0, 28708.5)])]
diff --git a/silo/benchmarks/results/istc3-7-27-13.py b/silo/benchmarks/results/istc3-7-27-13.py
new file mode 100644 (file)
index 0000000..36193d2
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': True, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(27932.9, 25707.6, 0.0357025, 43.6693, 0.0), (27225.1, 25056.8, 0.0366308, 44.4688, 0.0), (26066.5, 23989.5, 0.038259, 51.7758, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': False, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(30559.4, 0.0, 0.0326457, 0.0, 0.0), (30975.7, 0.0, 0.0322002, 0.0, 0.0), (30263.2, 0.0, 0.0329574, 0.0, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': True, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(104089.0, 95747.0, 0.0383247, 52.3198, 4.4979), (103967.0, 95633.5, 0.0383704, 65.3959, 4.14833), (104624.0, 96239.2, 0.0381277, 52.0013, 4.71454)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': False, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(112168.0, 0.0, 0.0355806, 0.0, 5.63329), (111897.0, 0.0, 0.0356603, 0.0, 4.69996), (112498.0, 0.0, 0.0354715, 0.0, 4.73329)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': True, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(187507.0, 172508.0, 0.042552, 153.726, 7.24524), (193159.0, 177704.0, 0.0413084, 94.652, 8.22911), (186911.0, 171962.0, 0.042691, 127.489, 7.61311)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': False, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(215159.0, 0.0, 0.0371015, 0.0, 9.03326), (215075.0, 0.0, 0.0371126, 0.0, 9.09989), (215787.0, 0.0, 0.0369858, 0.0, 9.3166)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': True, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(274235.0, 252266.0, 0.0436398, 166.333, 11.3754), (273407.0, 251499.0, 0.0437674, 201.409, 10.809), (269463.0, 247877.0, 0.0444047, 248.662, 11.0905)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': False, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(319060.0, 0.0, 0.0375294, 0.0, 13.3831), (313453.0, 0.0, 0.0381976, 0.0, 13.5831), (314425.0, 0.0, 0.0380777, 0.0, 13.5998)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': True, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(331782.0, 305201.0, 0.0477054, 859.518, 12.9499), (325504.0, 299429.0, 0.0486297, 898.045, 12.6247), (330611.0, 304123.0, 0.0479017, 897.195, 12.4785)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': False, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(412063.0, 0.0, 0.0387433, 0.0, 17.3998), (412653.0, 0.0, 0.0386915, 0.0, 17.7665), (414960.0, 0.0, 0.0384705, 0.0, 18.1831)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': True, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(351266.0, 323144.0, 0.0562856, 1207.4, 12.566), (358881.0, 330150.0, 0.054982, 1246.28, 12.3676), (356129.0, 327621.0, 0.055508, 1230.4, 11.84)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': False, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(503789.0, 0.0, 0.0396117, 0.0, 23.033), (502896.0, 0.0, 0.0396838, 0.0, 22.1998), (491385.0, 0.0, 0.0406139, 0.0, 22.6164)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': True, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(377349.0, 347108.0, 0.0623337, 1848.23, 12.2314), (384724.0, 353894.0, 0.0610587, 2023.77, 12.5417), (380727.0, 350213.0, 0.0620179, 1863.16, 11.978)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': False, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(572058.0, 0.0, 0.0418715, 0.0, 25.5163), (595977.0, 0.0, 0.0401823, 0.0, 25.7497), (570526.0, 0.0, 0.0419793, 0.0, 24.783)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': True, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(392417.0, 360987.0, 0.0696299, 2461.2, 12.0009), (391837.0, 360453.0, 0.0695999, 2344.58, 12.6564), (390281.0, 359022.0, 0.069838, 2547.39, 11.9206)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': False, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(649335.0, 0.0, 0.0430346, 0.0, 28.4162), (652786.0, 0.0, 0.0428061, 0.0, 28.8829), (646301.0, 0.0, 0.0432359, 0.0, 29.3662)]),({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': True, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(380778.0, 350266.0, 0.0815702, 5329.71, 11.6806), (381149.0, 350607.0, 0.0806715, 4643.52, 12.0704), (380764.0, 350254.0, 0.080364, 5234.89, 11.7478)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': False, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(714670.0, 0.0, 0.0446695, 0.0, 32.466), (731601.0, 0.0, 0.0436383, 0.0, 32.7657), (717230.0, 0.0, 0.0445168, 0.0, 31.9157)])]
diff --git a/silo/benchmarks/results/istc3-7-31-13.py b/silo/benchmarks/results/istc3-7-31-13.py
new file mode 100644 (file)
index 0000000..2b5818d
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': True, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(29172.5, 29172.5, 0.0341824, 43.0112, 0.0), (29398.3, 29398.3, 0.0339165, 42.9486, 0.0), (28847.7, 28847.7, 0.0345633, 42.8973, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': False, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(30468.6, 30468.6, 0.0327431, 0.0, 0.0), (29992.4, 29992.4, 0.0332567, 0.0, 0.0), (31170.2, 31170.2, 0.0320002, 0.0, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': True, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(103422.0, 103422.0, 0.0385643, 85.2116, 4.06392), (105666.0, 105666.0, 0.0377509, 60.8138, 3.93148), (104045.0, 104045.0, 0.0383381, 69.674, 4.63118)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': False, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(113136.0, 113136.0, 0.0352708, 0.0, 4.78329), (114626.0, 114626.0, 0.0348127, 0.0, 4.7333), (114772.0, 114772.0, 0.0347643, 0.0, 5.1833)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': True, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(197185.0, 197185.0, 0.0404586, 94.891, 8.41167), (197842.0, 197842.0, 0.0403221, 115.439, 7.94573), (197553.0, 197553.0, 0.0402832, 145.298, 8.4924)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': False, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(216956.0, 216956.0, 0.0367886, 0.0, 9.64994), (216944.0, 216944.0, 0.0367856, 0.0, 9.98321), (215150.0, 215150.0, 0.0370956, 0.0, 9.59995)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': True, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(296988.0, 296988.0, 0.0402945, 167.935, 12.127), (297616.0, 297616.0, 0.0402009, 213.303, 12.1242), (295502.0, 295502.0, 0.0404885, 258.694, 12.6107)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': False, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(316547.0, 316547.0, 0.0377822, 0.0, 13.4998), (321822.0, 321822.0, 0.0371894, 0.0, 13.4833), (318126.0, 318126.0, 0.037625, 0.0, 13.4665)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': True, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(391746.0, 391746.0, 0.0407244, 169.879, 16.0217), (386561.0, 386561.0, 0.041277, 181.483, 15.2751), (389939.0, 389939.0, 0.0409258, 162.949, 15.6935)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': False, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(423632.0, 423632.0, 0.0376798, 0.0, 18.3165), (425391.0, 425391.0, 0.0375244, 0.0, 18.3499), (422392.0, 422392.0, 0.0377958, 0.0, 17.9999)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': True, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(477401.0, 477401.0, 0.0417748, 99.8102, 18.5246), (484175.0, 484175.0, 0.0411903, 195.776, 19.9578), (485011.0, 485011.0, 0.0411185, 265.607, 20.8213)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': False, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(536948.0, 536948.0, 0.0371455, 0.0, 22.483), (538513.0, 538513.0, 0.0370509, 0.0, 23.383), (532447.0, 532447.0, 0.0374746, 0.0, 22.3997)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': True, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(573385.0, 573385.0, 0.0413281, 431.492, 23.0567), (570228.0, 570228.0, 0.041975, 220.776, 22.7373), (573395.0, 573395.0, 0.0417394, 145.422, 23.4863)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': False, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(632495.0, 632495.0, 0.0378555, 0.0, 27.383), (638658.0, 638658.0, 0.0374896, 0.0, 26.983), (631381.0, 631381.0, 0.0379224, 0.0, 26.5997)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': True, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(644206.0, 644206.0, 0.0427002, 1196.09, 26.5088), (635368.0, 635368.0, 0.0431587, 940.665, 26.4838), (645459.0, 645459.0, 0.0421841, 1584.15, 25.8248)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': False, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(728954.0, 728954.0, 0.0383087, 0.0, 31.2495), (730546.0, 730546.0, 0.0382386, 0.0, 30.0829), (731333.0, 731333.0, 0.0381883, 0.0, 32.0495)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': True, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(681638.0, 681638.0, 0.0464162, 935.341, 28.1929), (683559.0, 683559.0, 0.0464315, 731.81, 29.2552), (678029.0, 678029.0, 0.0468788, 612.582, 27.1238)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': False, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(799321.0, 799321.0, 0.0399277, 0.0, 34.0809), (799648.0, 799648.0, 0.0399073, 0.0, 35.415), (803230.0, 803230.0, 0.0397377, 0.0, 34.6826)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13.py b/silo/benchmarks/results/istc3-8-1-13.py
new file mode 100644 (file)
index 0000000..b05af70
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': True, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(29381.4, 29381.4, 0.0339166, 83.9258, 0.0), (28600.6, 28600.6, 0.0348584, 82.6534, 0.0), (28147.4, 28147.4, 0.0354085, 85.7701, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': False, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(30286.9, 30286.9, 0.0329245, 0.0, 0.0), (30753.4, 30753.4, 0.0324305, 0.0, 0.0), (31151.2, 31151.2, 0.0320159, 0.0, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': True, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(104443.0, 104443.0, 0.0381788, 96.0151, 4.16338), (105184.0, 105184.0, 0.0379089, 107.722, 4.34632), (101418.0, 101418.0, 0.0393277, 97.5319, 4.39737)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': False, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(112481.0, 112481.0, 0.0354717, 0.0, 5.06663), (111014.0, 111014.0, 0.0359431, 0.0, 5.14995), (112559.0, 112559.0, 0.035451, 0.0, 4.39996)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': True, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(195183.0, 195183.0, 0.0408511, 165.482, 8.40715), (195429.0, 195429.0, 0.0407978, 116.612, 7.95758), (198778.0, 198778.0, 0.040128, 141.342, 8.44383)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': False, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(209400.0, 209400.0, 0.0381135, 0.0, 9.3166), (209405.0, 209405.0, 0.0381132, 0.0, 9.03321), (214395.0, 214395.0, 0.0372271, 0.0, 9.49995)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': True, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(295485.0, 295485.0, 0.0404785, 160.172, 11.622), (294952.0, 294952.0, 0.040542, 137.536, 12.9175), (298367.0, 298367.0, 0.0400908, 152.179, 11.6397)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': False, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(317759.0, 317759.0, 0.0376752, 0.0, 13.5665), (319487.0, 319487.0, 0.0374694, 0.0, 13.5165), (323316.0, 323316.0, 0.0370279, 0.0, 13.9331)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': True, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(392681.0, 392681.0, 0.0406083, 178.028, 16.3166), (390808.0, 390808.0, 0.0408062, 134.08, 15.9497), (392443.0, 392443.0, 0.0406377, 143.774, 15.5671)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': False, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(420957.0, 420957.0, 0.0379212, 0.0, 18.2664), (419207.0, 419207.0, 0.0380766, 0.0, 18.8332), (421561.0, 421561.0, 0.0378607, 0.0, 17.6499)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': True, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(487133.0, 487133.0, 0.040897, 182.749, 20.3996), (482100.0, 482100.0, 0.0413421, 149.776, 19.9923), (482364.0, 482364.0, 0.0413235, 147.405, 18.6297)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': False, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(526460.0, 526460.0, 0.0378989, 0.0, 22.0331), (529197.0, 529197.0, 0.0377043, 0.0, 23.7497), (533214.0, 533214.0, 0.0374174, 0.0, 22.3831)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': True, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(576874.0, 576874.0, 0.0414611, 190.94, 23.9386), (568120.0, 568120.0, 0.0420953, 199.285, 22.8701), (580741.0, 580741.0, 0.0411825, 255.775, 23.1551)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': False, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(625632.0, 625632.0, 0.0382689, 0.0, 26.5161), (631692.0, 631692.0, 0.0378988, 0.0, 26.8828), (622284.0, 622284.0, 0.038478, 0.0, 26.9162)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': True, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(666090.0, 666090.0, 0.0418768, 120.662, 27.6226), (652526.0, 652526.0, 0.0427807, 184.736, 27.5441), (664060.0, 664060.0, 0.0420135, 121.436, 26.78)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': False, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(728676.0, 728676.0, 0.0383326, 0.0, 30.5828), (731237.0, 731237.0, 0.0382001, 0.0, 32.2995), (724542.0, 724542.0, 0.0385532, 0.0, 32.4161)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': True, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(675819.0, 675819.0, 0.0471086, 385.01, 27.8739), (681825.0, 681825.0, 0.0466471, 564.667, 28.1586), (677843.0, 677843.0, 0.0469547, 344.102, 27.9294)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': False, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(783983.0, 783983.0, 0.0407091, 0.0, 35.0656), (782308.0, 782308.0, 0.040797, 0.0, 33.7652), (715915.0, 715915.0, 0.0445905, 0.0, 31.1658)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13_compress.py b/silo/benchmarks/results/istc3-8-1-13_compress.py
new file mode 100644 (file)
index 0000000..07c880c
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': True, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': True}, [(26529.8, 26529.8, 0.0375845, 86.9875, 0.0), (26590.3, 26590.3, 0.0374976, 96.7505, 0.0), (25907.2, 25907.2, 0.0384877, 89.7372, 0.0)]), ({'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': True, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': True}, [(94923.0, 94923.0, 0.0420244, 118.105, 3.9137), (96772.3, 96772.3, 0.0412222, 99.4943, 3.66408), (97558.4, 97558.4, 0.040879, 98.773, 3.61314)]), ({'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': True, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': True}, [(180752.0, 180752.0, 0.0441143, 155.911, 7.25652), (183287.0, 183287.0, 0.0435169, 135.895, 7.32585), (183258.0, 183258.0, 0.0435234, 196.72, 7.24199)]), ({'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': True, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': True}, [(277963.0, 277963.0, 0.0430299, 165.512, 10.3201), (275547.0, 275547.0, 0.0434273, 155.312, 11.0243), (275690.0, 275690.0, 0.0434118, 117.571, 10.8426)]), ({'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': True, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': True}, [(363296.0, 363296.0, 0.0439195, 139.954, 13.9389), (365580.0, 365580.0, 0.0436271, 124.046, 14.7982), (367573.0, 367573.0, 0.0433953, 124.731, 14.1017)]), ({'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': True, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': True}, [(454856.0, 454856.0, 0.0438369, 148.817, 17.5135), (457039.0, 457039.0, 0.0436352, 164.887, 18.0665), (458381.0, 458381.0, 0.0435093, 111.431, 17.9343)]), ({'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': True, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': True}, [(543068.0, 543068.0, 0.0440688, 135.805, 19.8644), (547803.0, 547803.0, 0.0436806, 225.355, 21.0608), (547088.0, 547088.0, 0.043648, 175.741, 21.4165)]), ({'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': True, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': True}, [(627272.0, 627272.0, 0.0445038, 143.691, 25.3725), (627820.0, 627820.0, 0.0444822, 115.447, 24.7481), (626374.0, 626374.0, 0.0445666, 139.218, 23.8384)]), ({'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'log_compress': True, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': True}, [(668864.0, 668864.0, 0.0476782, 298.516, 24.8858), (659859.0, 659859.0, 0.0483394, 298.576, 24.3908), (654704.0, 654704.0, 0.0487128, 326.656, 25.9193)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13_fake_compress.py b/silo/benchmarks/results/istc3-8-1-13_fake_compress.py
new file mode 100644 (file)
index 0000000..276b001
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': True}, [(30760.5, 30760.5, 0.0324276, 82.5433, 0.0), (30830.7, 30830.7, 0.0323362, 82.3814, 0.0), (29312.7, 29312.7, 0.0340088, 82.4001, 0.0)]), ({'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': True}, [(110352.0, 110352.0, 0.0361334, 91.0361, 4.8453), (111290.0, 111290.0, 0.0358331, 96.3705, 4.52919), (110907.0, 110907.0, 0.0359571, 94.8387, 4.3793)]), ({'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': True}, [(207763.0, 207763.0, 0.0383983, 93.3717, 8.80995), (209884.0, 209884.0, 0.0380069, 98.4276, 9.32508), (209859.0, 209859.0, 0.037999, 98.5817, 8.65653)]), ({'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': True}, [(315501.0, 315501.0, 0.0379128, 96.7681, 14.1495), (295676.0, 295676.0, 0.0404589, 98.5001, 12.9538), (315340.0, 315340.0, 0.0379286, 96.5519, 14.0337)]), ({'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': True}, [(413379.0, 413379.0, 0.0385963, 98.742, 17.3198), (414454.0, 414454.0, 0.0384963, 98.645, 17.3375), (414772.0, 414772.0, 0.0384444, 100.227, 19.1244)]), ({'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': True}, [(517698.0, 517698.0, 0.0385251, 101.462, 22.7332), (513117.0, 513117.0, 0.0388595, 100.966, 21.6614), (521825.0, 521825.0, 0.0382079, 96.0045, 21.361)]), ({'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': True}, [(605309.0, 605309.0, 0.0395329, 97.6194, 25.9271), (616914.0, 616914.0, 0.0388073, 100.143, 26.5247), (612067.0, 612067.0, 0.0390815, 111.008, 25.9332)]), ({'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': True}, [(715009.0, 715009.0, 0.0390486, 98.5921, 31.4732), (717122.0, 717122.0, 0.0389269, 98.6476, 32.4048), (710089.0, 710089.0, 0.0393021, 96.557, 31.5093)]), ({'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': True}, [(780150.0, 780150.0, 0.0408795, 373.399, 34.0451), (780928.0, 780928.0, 0.0408545, 760.954, 33.7256), (773479.0, 773479.0, 0.0412565, 437.326, 33.7992)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13_fake_writes.py b/silo/benchmarks/results/istc3-8-1-13_fake_writes.py
new file mode 100644 (file)
index 0000000..a1be172
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': True}, [(29412.6, 29412.6, 0.033882, 81.7596, 0.0), (28554.8, 28554.8, 0.0349042, 81.5329, 0.0), (28620.5, 28620.5, 0.0348216, 81.6942, 0.0)]), ({'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': True}, [(104242.0, 104242.0, 0.0382459, 93.481, 4.3291), (103901.0, 103901.0, 0.0383728, 92.4471, 4.32914), (105468.0, 105468.0, 0.0378021, 92.8918, 4.47911)]), ({'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': True}, [(197567.0, 197567.0, 0.0403659, 97.7048, 8.17584), (198024.0, 198024.0, 0.0402751, 98.1746, 8.15999), (196321.0, 196321.0, 0.0406066, 100.727, 8.45577)]), ({'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': True}, [(301970.0, 301970.0, 0.0396107, 95.9425, 13.1208), (296951.0, 296951.0, 0.0402707, 101.857, 12.3516), (300682.0, 300682.0, 0.0397723, 100.84, 13.501)]), ({'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': True}, [(390917.0, 390917.0, 0.0407861, 100.397, 16.1469), (390454.0, 390454.0, 0.0408575, 102.181, 15.0578), (393603.0, 393603.0, 0.0405154, 94.5263, 16.8823)]), ({'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': True}, [(487936.0, 487936.0, 0.0408634, 101.266, 19.8535), (489413.0, 489413.0, 0.0407335, 97.2174, 20.2291), (487987.0, 487987.0, 0.0408372, 97.1397, 20.5744)]), ({'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': True}, [(584191.0, 584191.0, 0.0409526, 103.392, 23.4635), (587157.0, 587157.0, 0.0407409, 102.503, 24.2748), (586074.0, 586074.0, 0.0408189, 104.875, 25.1599)]), ({'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': True}, [(678667.0, 678667.0, 0.0411235, 99.0795, 27.6891), (677039.0, 677039.0, 0.0412212, 99.8785, 27.4051), (670260.0, 670260.0, 0.0416408, 105.28, 27.4072)]), ({'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': True}, [(722906.0, 722906.0, 0.0441231, 225.342, 28.614), (724204.0, 724204.0, 0.0440198, 236.26, 29.4157), (728319.0, 728319.0, 0.0437896, 237.912, 29.5433)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': False, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(30286.9, 30286.9, 0.0329245, 0.0, 0.0), (30753.4, 30753.4, 0.0324305, 0.0, 0.0), (31151.2, 31151.2, 0.0320159, 0.0, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': False, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(112481.0, 112481.0, 0.0354717, 0.0, 5.06663), (111014.0, 111014.0, 0.0359431, 0.0, 5.14995), (112559.0, 112559.0, 0.035451, 0.0, 4.39996)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': False, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(209400.0, 209400.0, 0.0381135, 0.0, 9.3166), (209405.0, 209405.0, 0.0381132, 0.0, 9.03321), (214395.0, 214395.0, 0.0372271, 0.0, 9.49995)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': False, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(317759.0, 317759.0, 0.0376752, 0.0, 13.5665), (319487.0, 319487.0, 0.0374694, 0.0, 13.5165), (323316.0, 323316.0, 0.0370279, 0.0, 13.9331)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': False, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(420957.0, 420957.0, 0.0379212, 0.0, 18.2664), (419207.0, 419207.0, 0.0380766, 0.0, 18.8332), (421561.0, 421561.0, 0.0378607, 0.0, 17.6499)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': False, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(526460.0, 526460.0, 0.0378989, 0.0, 22.0331), (529197.0, 529197.0, 0.0377043, 0.0, 23.7497), (533214.0, 533214.0, 0.0374174, 0.0, 22.3831)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': False, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(625632.0, 625632.0, 0.0382689, 0.0, 26.5161), (631692.0, 631692.0, 0.0378988, 0.0, 26.8828), (622284.0, 622284.0, 0.038478, 0.0, 26.9162)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': False, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(728676.0, 728676.0, 0.0383326, 0.0, 30.5828), (731237.0, 731237.0, 0.0382001, 0.0, 32.2995), (724542.0, 724542.0, 0.0385532, 0.0, 32.4161)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': False, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(783983.0, 783983.0, 0.0407091, 0.0, 35.0656), (782308.0, 782308.0, 0.040797, 0.0, 33.7652), (715915.0, 715915.0, 0.0445905, 0.0, 31.1658)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13_fake_writes_stride.py b/silo/benchmarks/results/istc3-8-1-13_fake_writes_stride.py
new file mode 100644 (file)
index 0000000..e0b4874
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': True}, [(28307.9, 28307.9, 0.0351975, 79.8686, 0.0), (29468.8, 29468.8, 0.0338098, 79.6989, 0.0), (28633.6, 28633.6, 0.0348038, 79.6323, 0.0)]), ({'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': True}, [(104982.0, 104982.0, 0.0379605, 93.4454, 3.94635), (105986.0, 105986.0, 0.0375971, 91.3793, 4.42884), (105113.0, 105113.0, 0.0379221, 92.7033, 4.24529)]), ({'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': True}, [(199243.0, 199243.0, 0.0400266, 94.1939, 7.99375), (201042.0, 201042.0, 0.0396748, 94.4927, 8.71124), (200883.0, 200883.0, 0.0396838, 97.8034, 8.74218)]), ({'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': True}, [(295769.0, 295769.0, 0.0404389, 104.279, 12.0241), (299185.0, 299185.0, 0.0399775, 102.402, 11.5576), (297104.0, 297104.0, 0.0402344, 101.553, 13.0691)]), ({'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': True}, [(390716.0, 390716.0, 0.0408101, 103.202, 16.7986), (388866.0, 388866.0, 0.0409949, 97.7126, 15.7795), (389102.0, 389102.0, 0.0409839, 102.555, 16.0022)]), ({'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': True}, [(487630.0, 487630.0, 0.0408567, 100.113, 20.5283), (486838.0, 486838.0, 0.0409294, 101.562, 20.4595), (485790.0, 485790.0, 0.0410379, 105.509, 19.7705)]), ({'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': True}, [(588835.0, 588835.0, 0.0406064, 99.1126, 23.9914), (586711.0, 586711.0, 0.0407728, 101.417, 23.647), (582473.0, 582473.0, 0.0410752, 102.085, 24.7148)]), ({'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': True}, [(675848.0, 675848.0, 0.0412852, 101.447, 28.2844), (677514.0, 677514.0, 0.041175, 98.7786, 30.5281), (676236.0, 676236.0, 0.0412817, 99.7915, 28.8308)]), ({'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': True}, [(723982.0, 723982.0, 0.044027, 236.204, 29.7214), (722847.0, 722847.0, 0.0441212, 307.379, 29.3968), (728086.0, 728086.0, 0.0437917, 235.66, 31.3378)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': False, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(30286.9, 30286.9, 0.0329245, 0.0, 0.0), (30753.4, 30753.4, 0.0324305, 0.0, 0.0), (31151.2, 31151.2, 0.0320159, 0.0, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': False, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(112481.0, 112481.0, 0.0354717, 0.0, 5.06663), (111014.0, 111014.0, 0.0359431, 0.0, 5.14995), (112559.0, 112559.0, 0.035451, 0.0, 4.39996)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': False, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(209400.0, 209400.0, 0.0381135, 0.0, 9.3166), (209405.0, 209405.0, 0.0381132, 0.0, 9.03321), (214395.0, 214395.0, 0.0372271, 0.0, 9.49995)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': False, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(317759.0, 317759.0, 0.0376752, 0.0, 13.5665), (319487.0, 319487.0, 0.0374694, 0.0, 13.5165), (323316.0, 323316.0, 0.0370279, 0.0, 13.9331)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': False, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(420957.0, 420957.0, 0.0379212, 0.0, 18.2664), (419207.0, 419207.0, 0.0380766, 0.0, 18.8332), (421561.0, 421561.0, 0.0378607, 0.0, 17.6499)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': False, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(526460.0, 526460.0, 0.0378989, 0.0, 22.0331), (529197.0, 529197.0, 0.0377043, 0.0, 23.7497), (533214.0, 533214.0, 0.0374174, 0.0, 22.3831)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': False, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(625632.0, 625632.0, 0.0382689, 0.0, 26.5161), (631692.0, 631692.0, 0.0378988, 0.0, 26.8828), (622284.0, 622284.0, 0.038478, 0.0, 26.9162)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': False, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(728676.0, 728676.0, 0.0383326, 0.0, 30.5828), (731237.0, 731237.0, 0.0382001, 0.0, 32.2995), (724542.0, 724542.0, 0.0385532, 0.0, 32.4161)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': False, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(783983.0, 783983.0, 0.0407091, 0.0, 35.0656), (782308.0, 782308.0, 0.040797, 0.0, 33.7652), (715915.0, 715915.0, 0.0445905, 0.0, 31.1658)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13_fake_writes_stride1.py b/silo/benchmarks/results/istc3-8-1-13_fake_writes_stride1.py
new file mode 100644 (file)
index 0000000..f6e99a8
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': True}, [(29161.1, 29161.1, 0.0341828, 79.6502, 0.0), (29596.2, 29596.2, 0.033672, 78.2933, 0.0), (30294.6, 30294.6, 0.0328931, 80.0015, 0.0)]), ({'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': True}, [(104680.0, 104680.0, 0.0380678, 93.543, 4.26127), (105357.0, 105357.0, 0.037833, 93.479, 4.24594), (105311.0, 105311.0, 0.0378461, 92.8869, 4.39667)]), ({'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': True}, [(199130.0, 199130.0, 0.040013, 100.575, 8.08887), (200940.0, 200940.0, 0.0396728, 100.988, 8.22472), (200066.0, 200066.0, 0.0398625, 100.844, 8.84284)]), ({'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': True}, [(300183.0, 300183.0, 0.0398305, 104.51, 14.0334), (302015.0, 302015.0, 0.0396003, 105.269, 12.0563), (301133.0, 301133.0, 0.0397071, 105.661, 12.3025)]), ({'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': True}, [(394222.0, 394222.0, 0.0404486, 110.365, 16.5004), (396748.0, 396748.0, 0.0401852, 106.095, 15.9532), (394091.0, 394091.0, 0.0404697, 107.902, 16.0202)]), ({'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': True}, [(492902.0, 492902.0, 0.0404255, 110.144, 20.2965), (493765.0, 493765.0, 0.0403417, 112.555, 19.5578), (492462.0, 492462.0, 0.0404569, 108.249, 19.24)]), ({'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': True}, [(588773.0, 588773.0, 0.0406194, 111.398, 24.3063), (590348.0, 590348.0, 0.0404954, 112.12, 25.0197), (583694.0, 583694.0, 0.0409702, 112.481, 23.4229)]), ({'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': True}, [(680396.0, 680396.0, 0.0410039, 112.546, 28.3629), (679657.0, 679657.0, 0.0410506, 114.858, 27.5391), (680564.0, 680564.0, 0.0410109, 115.396, 27.794)]), ({'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'bench_opts': '', 'log_fake_writes': True, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': True}, [(652861.0, 652861.0, 0.048789, 276.402, 27.5094), (651525.0, 651525.0, 0.0488993, 279.703, 27.6233), (654939.0, 654939.0, 0.0485726, 289.37, 27.9035)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': False, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(30286.9, 30286.9, 0.0329245, 0.0, 0.0), (30753.4, 30753.4, 0.0324305, 0.0, 0.0), (31151.2, 31151.2, 0.0320159, 0.0, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': False, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(112481.0, 112481.0, 0.0354717, 0.0, 5.06663), (111014.0, 111014.0, 0.0359431, 0.0, 5.14995), (112559.0, 112559.0, 0.035451, 0.0, 4.39996)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': False, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(209400.0, 209400.0, 0.0381135, 0.0, 9.3166), (209405.0, 209405.0, 0.0381132, 0.0, 9.03321), (214395.0, 214395.0, 0.0372271, 0.0, 9.49995)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': False, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(317759.0, 317759.0, 0.0376752, 0.0, 13.5665), (319487.0, 319487.0, 0.0374694, 0.0, 13.5165), (323316.0, 323316.0, 0.0370279, 0.0, 13.9331)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': False, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(420957.0, 420957.0, 0.0379212, 0.0, 18.2664), (419207.0, 419207.0, 0.0380766, 0.0, 18.8332), (421561.0, 421561.0, 0.0378607, 0.0, 17.6499)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': False, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(526460.0, 526460.0, 0.0378989, 0.0, 22.0331), (529197.0, 529197.0, 0.0377043, 0.0, 23.7497), (533214.0, 533214.0, 0.0374174, 0.0, 22.3831)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': False, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(625632.0, 625632.0, 0.0382689, 0.0, 26.5161), (631692.0, 631692.0, 0.0378988, 0.0, 26.8828), (622284.0, 622284.0, 0.038478, 0.0, 26.9162)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': False, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(728676.0, 728676.0, 0.0383326, 0.0, 30.5828), (731237.0, 731237.0, 0.0382001, 0.0, 32.2995), (724542.0, 724542.0, 0.0385532, 0.0, 32.4161)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': False, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(783983.0, 783983.0, 0.0407091, 0.0, 35.0656), (782308.0, 782308.0, 0.040797, 0.0, 33.7652), (715915.0, 715915.0, 0.0445905, 0.0, 31.1658)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13_log_reduce_size.py b/silo/benchmarks/results/istc3-8-1-13_log_reduce_size.py
new file mode 100644 (file)
index 0000000..fa36665
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': True}, [(28037.0, 28037.0, 0.0355513, 80.7772, 0.0), (28591.2, 28591.2, 0.0348332, 83.3726, 0.0), (28174.9, 28174.9, 0.0353661, 80.9923, 0.0)]), ({'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': True}, [(101575.0, 101575.0, 0.0392489, 100.616, 4.06351), (104315.0, 104315.0, 0.0382123, 92.747, 4.44552), (103600.0, 103600.0, 0.0384777, 104.11, 4.54582)]), ({'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': True}, [(198630.0, 198630.0, 0.0401546, 114.32, 8.11104), (201243.0, 201243.0, 0.039627, 156.711, 7.31041), (199076.0, 199076.0, 0.0400534, 119.783, 8.10939)]), ({'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': True}, [(298566.0, 298566.0, 0.0400453, 133.356, 12.1187), (298133.0, 298133.0, 0.040098, 99.2246, 13.4485), (296903.0, 296903.0, 0.0402781, 124.433, 12.3886)]), ({'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': True}, [(391792.0, 391792.0, 0.0407065, 118.188, 17.0175), (391965.0, 391965.0, 0.0406917, 111.37, 16.8899), (390145.0, 390145.0, 0.0408607, 110.049, 15.7134)]), ({'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': True}, [(490782.0, 490782.0, 0.0406097, 118.621, 19.6794), (491448.0, 491448.0, 0.0405582, 117.648, 19.7812), (490189.0, 490189.0, 0.0406614, 146.335, 19.8124)]), ({'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': True}, [(579773.0, 579773.0, 0.0412569, 163.993, 23.5593), (582446.0, 582446.0, 0.0410567, 105.998, 25.318), (576929.0, 576929.0, 0.0414494, 104.131, 23.6849)]), ({'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': True}, [(670070.0, 670070.0, 0.041655, 103.69, 27.1961), (669347.0, 669347.0, 0.0416867, 141.835, 27.8873), (669634.0, 669634.0, 0.0416696, 102.389, 26.9551)]), ({'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': True}, [(712434.0, 712434.0, 0.0447644, 214.95, 29.8936), (700382.0, 700382.0, 0.0455139, 240.664, 27.8856), (712480.0, 712480.0, 0.0447631, 234.26, 28.4791)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': False, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(30286.9, 30286.9, 0.0329245, 0.0, 0.0), (30753.4, 30753.4, 0.0324305, 0.0, 0.0), (31151.2, 31151.2, 0.0320159, 0.0, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': False, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(112481.0, 112481.0, 0.0354717, 0.0, 5.06663), (111014.0, 111014.0, 0.0359431, 0.0, 5.14995), (112559.0, 112559.0, 0.035451, 0.0, 4.39996)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': False, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(209400.0, 209400.0, 0.0381135, 0.0, 9.3166), (209405.0, 209405.0, 0.0381132, 0.0, 9.03321), (214395.0, 214395.0, 0.0372271, 0.0, 9.49995)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': False, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(317759.0, 317759.0, 0.0376752, 0.0, 13.5665), (319487.0, 319487.0, 0.0374694, 0.0, 13.5165), (323316.0, 323316.0, 0.0370279, 0.0, 13.9331)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': False, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(420957.0, 420957.0, 0.0379212, 0.0, 18.2664), (419207.0, 419207.0, 0.0380766, 0.0, 18.8332), (421561.0, 421561.0, 0.0378607, 0.0, 17.6499)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': False, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(526460.0, 526460.0, 0.0378989, 0.0, 22.0331), (529197.0, 529197.0, 0.0377043, 0.0, 23.7497), (533214.0, 533214.0, 0.0374174, 0.0, 22.3831)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': False, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(625632.0, 625632.0, 0.0382689, 0.0, 26.5161), (631692.0, 631692.0, 0.0378988, 0.0, 26.8828), (622284.0, 622284.0, 0.038478, 0.0, 26.9162)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': False, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(728676.0, 728676.0, 0.0383326, 0.0, 30.5828), (731237.0, 731237.0, 0.0382001, 0.0, 32.2995), (724542.0, 724542.0, 0.0385532, 0.0, 32.4161)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': False, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(783983.0, 783983.0, 0.0407091, 0.0, 35.0656), (782308.0, 782308.0, 0.040797, 0.0, 33.7652), (715915.0, 715915.0, 0.0445905, 0.0, 31.1658)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13_log_reduce_size_1.py b/silo/benchmarks/results/istc3-8-1-13_log_reduce_size_1.py
new file mode 100644 (file)
index 0000000..879b448
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': True}, [(670070.0, 670070.0, 0.041655, 103.69, 27.1961), (669347.0, 669347.0, 0.0416867, 141.835, 27.8873), (669634.0, 669634.0, 0.0416696, 102.389, 26.9551)]), ({'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': True}, [(712434.0, 712434.0, 0.0447644, 214.95, 29.8936), (700382.0, 700382.0, 0.0455139, 240.664, 27.8856), (712480.0, 712480.0, 0.0447631, 234.26, 28.4791)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13_log_reduce_size_nofsync.py b/silo/benchmarks/results/istc3-8-1-13_log_reduce_size_nofsync.py
new file mode 100644 (file)
index 0000000..c50f258
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': True, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': True}, [(27871.4, 27871.4, 0.0357607, 81.8997, 0.0), (29390.7, 29390.7, 0.0338998, 82.0619, 0.0), (28280.2, 28280.2, 0.0352519, 82.0879, 0.0)]), ({'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': True, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': True}, [(107300.0, 107300.0, 0.0371596, 96.2916, 4.61302), (106620.0, 106620.0, 0.037399, 95.1791, 4.31287), (104450.0, 104450.0, 0.0381691, 94.9936, 4.39564)]), ({'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': True, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': True}, [(198942.0, 198942.0, 0.0400843, 110.726, 8.97438), (199131.0, 199131.0, 0.0400565, 105.12, 8.5101), (200443.0, 200443.0, 0.0397906, 103.181, 7.71011)]), ({'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': True, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': True}, [(299454.0, 299454.0, 0.0399389, 109.416, 12.6518), (299527.0, 299527.0, 0.0399373, 115.718, 12.2231), (300443.0, 300443.0, 0.0398378, 106.138, 12.145)]), ({'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': True, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': True}, [(391388.0, 391388.0, 0.0407652, 124.563, 15.2232), (394657.0, 394657.0, 0.0404256, 130.43, 15.0728), (396611.0, 396611.0, 0.0402169, 136.605, 16.1692)]), ({'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': True, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': True}, [(498099.0, 498099.0, 0.0400256, 134.432, 21.216), (488264.0, 488264.0, 0.0408347, 133.72, 20.5144), (495505.0, 495505.0, 0.0402241, 150.323, 21.0244)]), ({'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': True, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': True}, [(584701.0, 584701.0, 0.0409133, 102.95, 23.1438), (586412.0, 586412.0, 0.0407895, 99.3766, 22.822), (589676.0, 589676.0, 0.0405919, 99.7467, 22.6878)]), ({'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': True, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': True}, [(675436.0, 675436.0, 0.0413229, 105.453, 28.0404), (660534.0, 660534.0, 0.0422653, 103.337, 27.3102), (679870.0, 679870.0, 0.0410626, 101.341, 28.1444)]), ({'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': True, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': True}, [(711674.0, 711674.0, 0.0448229, 216.884, 28.8772), (715904.0, 715904.0, 0.0445292, 279.838, 29.7625), (725241.0, 725241.0, 0.0439786, 315.067, 29.7777)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'numa_memory': '4G', 'persist': False, 'threads': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(30286.9, 30286.9, 0.0329245, 0.0, 0.0), (30753.4, 30753.4, 0.0324305, 0.0, 0.0), (31151.2, 31151.2, 0.0320159, 0.0, 0.0)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'numa_memory': '16G', 'persist': False, 'threads': 4, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(112481.0, 112481.0, 0.0354717, 0.0, 5.06663), (111014.0, 111014.0, 0.0359431, 0.0, 5.14995), (112559.0, 112559.0, 0.035451, 0.0, 4.39996)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'numa_memory': '32G', 'persist': False, 'threads': 8, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(209400.0, 209400.0, 0.0381135, 0.0, 9.3166), (209405.0, 209405.0, 0.0381132, 0.0, 9.03321), (214395.0, 214395.0, 0.0372271, 0.0, 9.49995)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'numa_memory': '48G', 'persist': False, 'threads': 12, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(317759.0, 317759.0, 0.0376752, 0.0, 13.5665), (319487.0, 319487.0, 0.0374694, 0.0, 13.5165), (323316.0, 323316.0, 0.0370279, 0.0, 13.9331)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'numa_memory': '64G', 'persist': False, 'threads': 16, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(420957.0, 420957.0, 0.0379212, 0.0, 18.2664), (419207.0, 419207.0, 0.0380766, 0.0, 18.8332), (421561.0, 421561.0, 0.0378607, 0.0, 17.6499)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'numa_memory': '80G', 'persist': False, 'threads': 20, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(526460.0, 526460.0, 0.0378989, 0.0, 22.0331), (529197.0, 529197.0, 0.0377043, 0.0, 23.7497), (533214.0, 533214.0, 0.0374174, 0.0, 22.3831)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'numa_memory': '96G', 'persist': False, 'threads': 24, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(625632.0, 625632.0, 0.0382689, 0.0, 26.5161), (631692.0, 631692.0, 0.0378988, 0.0, 26.8828), (622284.0, 622284.0, 0.038478, 0.0, 26.9162)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'numa_memory': '112G', 'persist': False, 'threads': 28, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(728676.0, 728676.0, 0.0383326, 0.0, 30.5828), (731237.0, 731237.0, 0.0382001, 0.0, 32.2995), (724542.0, 724542.0, 0.0385532, 0.0, 32.4161)]), ({'par_load': False, 'bench_opts': '', 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'numa_memory': '128G', 'persist': False, 'threads': 32, 'db': 'ndb-proto2', 'bench': 'tpcc'}, [(783983.0, 783983.0, 0.0407091, 0.0, 35.0656), (782308.0, 782308.0, 0.040797, 0.0, 33.7652), (715915.0, 715915.0, 0.0445905, 0.0, 31.1658)])]
diff --git a/silo/benchmarks/results/istc3-8-1-13_newbench.py b/silo/benchmarks/results/istc3-8-1-13_newbench.py
new file mode 100644 (file)
index 0000000..44a0ee8
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': True}, [(28105.9, 28105.9, 0.0354625, 85.3912, 0.0), (27718.0, 27718.0, 0.0359689, 82.7861, 0.0), (27949.1, 27949.1, 0.0356488, 83.5573, 0.0)]), ({'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': True}, [(104873.0, 104873.0, 0.0380279, 94.162, 3.51415), (102970.0, 102970.0, 0.0387283, 97.7687, 3.7135), (103883.0, 103883.0, 0.0383696, 97.6079, 3.54536)]), ({'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': True}, [(198633.0, 198633.0, 0.040137, 133.583, 6.62501), (197752.0, 197752.0, 0.0403254, 164.783, 7.04287), (197610.0, 197610.0, 0.0403784, 112.569, 7.03025)]), ({'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': True}, [(293988.0, 293988.0, 0.0406976, 149.569, 11.0425), (297583.0, 297583.0, 0.0401963, 159.338, 10.6732), (296544.0, 296544.0, 0.0403402, 144.554, 11.2229)]), ({'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': True}, [(394394.0, 394394.0, 0.0404466, 134.949, 14.3219), (390208.0, 390208.0, 0.0408714, 141.003, 14.5685), (384651.0, 384651.0, 0.0408262, 156.101, 13.5742)]), ({'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': True}, [(494616.0, 494616.0, 0.0402946, 237.19, 19.4914), (492169.0, 492169.0, 0.0404922, 154.481, 18.2076), (490409.0, 490409.0, 0.0406409, 174.401, 17.9603)]), ({'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': True}, [(581661.0, 581661.0, 0.0411126, 267.669, 21.6865), (583849.0, 583849.0, 0.0409649, 210.809, 21.5225), (588341.0, 588341.0, 0.0406641, 216.731, 22.2954)]), ({'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': True}, [(675474.0, 675474.0, 0.0413055, 117.162, 25.3652), (673212.0, 673212.0, 0.0414435, 155.92, 25.0156), (675001.0, 675001.0, 0.0413424, 152.043, 25.6195)]), ({'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': True}, [(703933.0, 703933.0, 0.0452991, 310.755, 27.3752), (616223.0, 616223.0, 0.0517334, 174.221, 21.4519), (700472.0, 700472.0, 0.0455173, 274.073, 26.4876)])]
diff --git a/silo/benchmarks/results/istc3-8-12-13.py b/silo/benchmarks/results/istc3-8-12-13.py
new file mode 100644 (file)
index 0000000..42b466f
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(754498.0, 754498.0, 0.0370564, 0.0, 0.0), (746465.0, 746465.0, 0.0374603, 0.0, 0.0), (756534.0, 756534.0, 0.0369612, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(745609.0, 745609.0, 0.0374916, 0.0, 112.679), (744485.0, 744485.0, 0.0375438, 0.0, 114.331), (733110.0, 733110.0, 0.0381287, 0.0, 112.065)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(739070.0, 739070.0, 0.0378141, 0.0, 228.162), (738579.0, 738579.0, 0.0378392, 0.0, 220.711), (737564.0, 737564.0, 0.037888, 0.0, 227.395)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(735042.0, 735042.0, 0.038007, 0.0, 341.076), (734844.0, 734844.0, 0.0380208, 0.0, 332.127), (730155.0, 730155.0, 0.0382601, 0.0, 332.409)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(720893.0, 720893.0, 0.0387456, 0.0, 440.57), (731112.0, 731112.0, 0.0382058, 0.0, 441.189), (723148.0, 723148.0, 0.0386235, 0.0, 437.64)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(725284.0, 725284.0, 0.0385056, 0.0, 545.773), (726488.0, 726488.0, 0.038434, 0.0, 546.673), (725655.0, 725655.0, 0.0384744, 0.0, 545.304)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(723624.0, 723624.0, 0.0385806, 0.0, 646.588), (717315.0, 717315.0, 0.0389208, 0.0, 645.355), (717324.0, 717324.0, 0.0389211, 0.0, 646.07)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(718913.0, 718913.0, 0.0388216, 0.0, 751.371), (721175.0, 721175.0, 0.0386679, 0.0, 750.104), (721843.0, 721843.0, 0.0386718, 0.0, 752.353)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(721156.0, 721156.0, 0.0386928, 0.0, 861.668), (719578.0, 719578.0, 0.038781, 0.0, 849.779), (718361.0, 718361.0, 0.0388418, 0.0, 852.768)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(710609.0, 710609.0, 0.0392614, 0.0, 934.032), (717574.0, 717574.0, 0.0388821, 0.0, 949.949), (712622.0, 712622.0, 0.0391449, 0.0, 953.965)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(710516.0, 710516.0, 0.0392554, 0.0, 1034.8), (712859.0, 712859.0, 0.0391243, 0.0, 1047.85), (708679.0, 708679.0, 0.0393565, 0.0, 1043.46)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(899626.0, 899626.0, 0.0310747, 0.0, 0.0), (895546.0, 895546.0, 0.0312123, 0.0, 0.0), (899906.0, 899906.0, 0.0310638, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(884888.0, 884888.0, 0.0315844, 0.0, 131.431), (879121.0, 879121.0, 0.0317911, 0.0, 132.332), (882563.0, 882563.0, 0.0316622, 0.0, 134.131)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(864166.0, 864166.0, 0.0323348, 0.0, 256.629), (872042.0, 872042.0, 0.0320403, 0.0, 264.247), (875714.0, 875714.0, 0.0319054, 0.0, 265.612)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(865869.0, 865869.0, 0.0322577, 0.0, 391.429), (864799.0, 864799.0, 0.0322997, 0.0, 386.328), (860334.0, 860334.0, 0.0324662, 0.0, 385.975)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(860756.0, 860756.0, 0.0324453, 0.0, 510.692), (856401.0, 856401.0, 0.0326079, 0.0, 510.534), (856528.0, 856528.0, 0.0326045, 0.0, 510.741)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(857631.0, 857631.0, 0.0325564, 0.0, 630.224), (858923.0, 858923.0, 0.0325077, 0.0, 639.97), (859701.0, 859701.0, 0.0324736, 0.0, 637.432)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(854499.0, 854499.0, 0.0326659, 0.0, 753.373), (852208.0, 852208.0, 0.0327554, 0.0, 749.117), (855464.0, 855464.0, 0.032631, 0.0, 757.053)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(851898.0, 851898.0, 0.0327619, 0.0, 872.706), (845040.0, 845040.0, 0.033023, 0.0, 861.454), (844817.0, 844817.0, 0.0330335, 0.0, 871.272)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(843353.0, 843353.0, 0.0330803, 0.0, 980.98), (845069.0, 845069.0, 0.0330161, 0.0, 989.201), (841800.0, 841800.0, 0.0331458, 0.0, 982.938)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(840927.0, 840927.0, 0.0331699, 0.0, 1100.41), (845239.0, 845239.0, 0.0330054, 0.0, 1107.34), (836095.0, 836095.0, 0.0333309, 0.0, 1098.12)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(841338.0, 841338.0, 0.033152, 0.0, 1215.96), (840821.0, 840821.0, 0.0331683, 0.0, 1206.83), (839294.0, 839294.0, 0.0332273, 0.0, 1213.6)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(1213330.0, 1213330.0, 0.0230298, 0.0, 0.0), (1211230.0, 1211230.0, 0.023069, 0.0, 0.0), (1220470.0, 1220470.0, 0.0228953, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(1005770.0, 1005770.0, 0.027792, 0.0, 0.0), (1001880.0, 1001880.0, 0.0279012, 0.0, 0.0), (1000700.0, 1000700.0, 0.0279315, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(817871.0, 817871.0, 0.0341885, 0.0, 0.0), (832547.0, 832547.0, 0.0335844, 0.0, 0.0), (836308.0, 836308.0, 0.033433, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(685744.0, 685744.0, 0.0407839, 0.0, 0.0), (684120.0, 684120.0, 0.0408809, 0.0, 0.0), (679728.0, 679728.0, 0.0411451, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(562817.0, 562817.0, 0.0497014, 0.0, 0.0), (567602.0, 567602.0, 0.0492832, 0.0, 0.0), (557515.0, 557515.0, 0.0501742, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(475962.0, 475962.0, 0.0587792, 0.0, 0.0), (472583.0, 472583.0, 0.059199, 0.0, 0.0), (476018.0, 476018.0, 0.0587738, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(403760.0, 403760.0, 0.069299, 0.0, 0.0), (400413.0, 400413.0, 0.0698799, 0.0, 0.0), (403829.0, 403829.0, 0.0692871, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(350825.0, 350825.0, 0.0797634, 0.0, 0.0), (350289.0, 350289.0, 0.0798824, 0.0, 0.0), (346730.0, 346730.0, 0.0807037, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(308843.0, 308843.0, 0.0906093, 0.0, 0.0), (308218.0, 308218.0, 0.0907937, 0.0, 0.0), (307750.0, 307750.0, 0.090932, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(274893.0, 274893.0, 0.10181, 0.0, 0.0), (272930.0, 272930.0, 0.10254, 0.0, 0.0), (274165.0, 274165.0, 0.10208, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False}, [(242222.0, 242222.0, 0.115546, 0.0, 0.0), (246739.0, 246739.0, 0.113429, 0.0, 0.0), (243165.0, 243165.0, 0.115092, 0.0, 0.0)])] + [({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(933397.0, 933397.0, 0.0299485, 0.0, 0.0), (922014.0, 922014.0, 0.0303183, 0.0, 0.0), (930032.0, 930032.0, 0.0300562, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(921479.0, 921479.0, 0.0303282, 0.0, 139.547), (903039.0, 903039.0, 0.0309376, 0.0, 136.881), (926122.0, 926122.0, 0.0301663, 0.0, 141.646)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(910507.0, 910507.0, 0.0306879, 0.0, 278.213), (903208.0, 903208.0, 0.0309307, 0.0, 271.713), (913367.0, 913367.0, 0.0305799, 0.0, 276.696)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(908978.0, 908978.0, 0.0307281, 0.0, 415.544), (903596.0, 903596.0, 0.0309138, 0.0, 402.826), (896308.0, 896308.0, 0.0311641, 0.0, 400.592)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(906015.0, 906015.0, 0.0308213, 0.0, 539.525), (890827.0, 890827.0, 0.0313515, 0.0, 534.407), (890353.0, 890353.0, 0.0313647, 0.0, 532.776)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(891560.0, 891560.0, 0.0313047, 0.0, 662.078), (899829.0, 899829.0, 0.0310291, 0.0, 672.666), (897151.0, 897151.0, 0.0311158, 0.0, 665.573)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(895414.0, 895414.0, 0.031172, 0.0, 793.657), (892797.0, 892797.0, 0.0312603, 0.0, 793.273), (890886.0, 890886.0, 0.0313334, 0.0, 788.389)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(890289.0, 890289.0, 0.0313428, 0.0, 919.319), (888221.0, 888221.0, 0.031416, 0.0, 919.954), (889383.0, 889383.0, 0.0313763, 0.0, 921.856)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(884959.0, 884959.0, 0.0315292, 0.0, 1046.7), (876849.0, 876849.0, 0.0318198, 0.0, 1029.4), (885991.0, 885991.0, 0.0314921, 0.0, 1039.24)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(882921.0, 882921.0, 0.0315894, 0.0, 1159.72), (885280.0, 885280.0, 0.0314986, 0.0, 1168.1), (873920.0, 873920.0, 0.0319168, 0.0, 1151.48)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': True}, [(882499.0, 882499.0, 0.0315999, 0.0, 1280.98), (881339.0, 881339.0, 0.0316445, 0.0, 1279.68), (880616.0, 880616.0, 0.03166, 0.0, 1281.3)])]
diff --git a/silo/benchmarks/results/istc3-8-16-13_multipart_skew.py b/silo/benchmarks/results/istc3-8-16-13_multipart_skew.py
new file mode 100644 (file)
index 0000000..9038dc7
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'disable_gc': False, 'scale_factor': 4, 'db': 'kvdb-st', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks ', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': False, 'disable_snapshots': False}, [(43468.0, 43468.0, 0.0229553, 0.0, 0.0), (42505.9, 42505.9, 0.0234737, 0.0, 0.0), (43154.0, 43154.0, 0.0231245, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': False, 'disable_snapshots': False}, [(30754.7, 30754.7, 0.0324678, 0.0, 0.0), (30117.4, 30117.4, 0.0331507, 0.0, 0.0), (29800.0, 29800.0, 0.0335089, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 2, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '8G', 'persist': False, 'disable_snapshots': False}, [(61867.5, 61867.5, 0.0322773, 0.0, 3.19998), (62408.2, 62408.2, 0.0319925, 0.0, 3.06664), (61668.9, 61668.9, 0.0323773, 0.0, 2.96665)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': False, 'disable_snapshots': False}, [(116912.0, 116912.0, 0.0341545, 0.0, 17.1332), (113059.0, 113059.0, 0.0353051, 0.0, 17.3165), (115758.0, 115758.0, 0.0344966, 0.0, 17.2999)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 6, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '24G', 'persist': False, 'disable_snapshots': False}, [(159447.0, 159447.0, 0.0350288, 0.0, 9514.06), (159379.0, 159379.0, 0.0350259, 0.0, 9480.49), (159677.0, 159677.0, 0.0349627, 0.0, 9492.04)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': False, 'disable_snapshots': False}, [(199853.0, 199853.0, 0.0359222, 0.0, 18743.0), (198260.0, 198260.0, 0.0362176, 0.0, 18613.7), (197791.0, 197791.0, 0.0362914, 0.0, 18554.3)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 10, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '40G', 'persist': False, 'disable_snapshots': False}, [(179239.0, 179239.0, 0.0465518, 0.0, 27440.2), (175376.0, 175376.0, 0.0474159, 0.0, 27047.5), (179595.0, 179595.0, 0.0464574, 0.0, 27482.7)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': False, 'disable_snapshots': False}, [(137867.0, 137867.0, 0.0666319, 0.0, 28550.1), (137801.0, 137801.0, 0.0662968, 0.0, 28607.6), (140053.0, 140053.0, 0.0655534, 0.0, 29200.2)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': False, 'disable_snapshots': False}, [(110439.0, 110439.0, 0.0982468, 0.0, 35981.9), (110248.0, 110248.0, 0.0982313, 0.0, 35980.5), (111519.0, 111519.0, 0.0973957, 0.0, 36321.1)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': False, 'disable_snapshots': False}, [(58960.5, 58960.5, 0.189055, 0.0, 29293.0), (58409.4, 58409.4, 0.18975, 0.0, 28755.0), (56021.4, 56021.4, 0.199431, 0.0, 27870.6)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': False, 'disable_snapshots': False}, [(48373.0, 48373.0, 0.249847, 0.0, 30793.9), (48672.1, 48672.1, 0.2476, 0.0, 31244.2), (48770.2, 48770.2, 0.246519, 0.0, 31328.5)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': False}, [(27698.3, 27698.3, 0.429631, 0.0, 22725.1), (28482.3, 28482.3, 0.417004, 0.0, 23111.7), (27963.0, 27963.0, 0.427409, 0.0, 22980.8)])] + [({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': False, 'disable_snapshots': False}, [(30943.9, 30943.9, 0.0322558, 0.0, 0.0), (31465.1, 31465.1, 0.0317251, 0.0, 0.0), (31357.4, 31357.4, 0.0318378, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 2, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '8G', 'persist': False, 'disable_snapshots': False}, [(62177.6, 62177.6, 0.0321066, 0.0, 2.89999), (62432.7, 62432.7, 0.0319747, 0.0, 2.94999), (62648.0, 62648.0, 0.0318621, 0.0, 3.38331)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': False, 'disable_snapshots': False}, [(118895.0, 118895.0, 0.0335782, 0.0, 17.9332), (117912.0, 117912.0, 0.0338585, 0.0, 17.7666), (118688.0, 118688.0, 0.0336305, 0.0, 17.6999)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 6, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '24G', 'persist': False, 'disable_snapshots': False}, [(171283.0, 171283.0, 0.0346632, 0.0, 890.859), (170433.0, 170433.0, 0.0348388, 0.0, 873.825), (171277.0, 171277.0, 0.0346634, 0.0, 892.642)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': False, 'disable_snapshots': False}, [(219415.0, 219415.0, 0.0359304, 0.0, 1712.53), (219942.0, 219942.0, 0.0358451, 0.0, 1716.32), (218084.0, 218084.0, 0.0361459, 0.0, 1706.6)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 10, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '40G', 'persist': False, 'disable_snapshots': False}, [(206212.0, 206212.0, 0.0472844, 0.0, 2656.74), (205616.0, 205616.0, 0.047406, 0.0, 2657.5), (206888.0, 206888.0, 0.0471279, 0.0, 2660.49)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': False, 'disable_snapshots': False}, [(169345.0, 169345.0, 0.0685426, 0.0, 2789.03), (170003.0, 170003.0, 0.0683121, 0.0, 2803.62), (169983.0, 169983.0, 0.0683306, 0.0, 2804.06)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': False, 'disable_snapshots': False}, [(149677.0, 149677.0, 0.10188, 0.0, 3785.16), (145768.0, 145768.0, 0.104591, 0.0, 3691.9), (149610.0, 149610.0, 0.101906, 0.0, 3791.05)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': False, 'disable_snapshots': False}, [(83235.8, 83235.8, 0.22186, 0.0, 2809.16), (85806.4, 85806.4, 0.216144, 0.0, 2920.95), (88497.0, 88497.0, 0.209852, 0.0, 3036.64)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': False, 'disable_snapshots': False}, [(75410.7, 75410.7, 0.29186, 0.0, 3327.34), (81033.5, 81033.5, 0.271947, 0.0, 3516.03), (80770.0, 80770.0, 0.27314, 0.0, 3443.05)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': False, 'disable_snapshots': False}, [(52767.3, 52767.3, 0.47619, 0.0, 2730.02), (51759.0, 51759.0, 0.483218, 0.0, 2647.96), (51844.8, 51844.8, 0.483051, 0.0, 2664.81)])]
diff --git a/silo/benchmarks/results/istc3-8-19-13_cameraready.py b/silo/benchmarks/results/istc3-8-19-13_cameraready.py
new file mode 100644 (file)
index 0000000..8f05afb
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'disable_gc': False, 'scale_factor': 320000, 'db': 'kvdb', 'par_load': True, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '101G', 'persist': 'persist-none', 'disable_snapshots': False}, [(551002.0, 551002.0, 0.00176653, 0.0, 0.0), (538955.0, 538955.0, 0.00180647, 0.0, 0.0), (536552.0, 536552.0, 0.00181532, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto1', 'par_load': True, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '101G', 'persist': 'persist-none', 'disable_snapshots': False}, [(538072.0, 538072.0, 0.00181022, 0.0, 0.0), (528201.0, 528201.0, 0.00184491, 0.0, 0.0), (537755.0, 537755.0, 0.00181127, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto2', 'par_load': True, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '101G', 'persist': 'persist-none', 'disable_snapshots': False}, [(527898.0, 527898.0, 0.00184594, 0.0, 0.0), (528276.0, 528276.0, 0.00184426, 0.0, 0.0), (529182.0, 529182.0, 0.00184091, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'kvdb', 'par_load': True, 'threads': 4, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '105G', 'persist': 'persist-none', 'disable_snapshots': False}, [(2127010.0, 2127010.0, 0.00183115, 0.0, 0.0), (2117550.0, 2117550.0, 0.00183946, 0.0, 0.0), (2122640.0, 2122640.0, 0.00183503, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto1', 'par_load': True, 'threads': 4, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '105G', 'persist': 'persist-none', 'disable_snapshots': False}, [(2150000.0, 2150000.0, 0.00181072, 0.0, 0.0), (2150330.0, 2150330.0, 0.00181084, 0.0, 0.0), (2136830.0, 2136830.0, 0.00182216, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto2', 'par_load': True, 'threads': 4, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '105G', 'persist': 'persist-none', 'disable_snapshots': False}, [(2159790.0, 2159790.0, 0.00180276, 0.0, 0.0), (2158170.0, 2158170.0, 0.00180382, 0.0, 0.0), (2157110.0, 2157110.0, 0.00180512, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'kvdb', 'par_load': True, 'threads': 8, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '111G', 'persist': 'persist-none', 'disable_snapshots': False}, [(4072380.0, 4072380.0, 0.0019148, 0.0, 0.0), (4067130.0, 4067130.0, 0.00191734, 0.0, 0.0), (4074560.0, 4074560.0, 0.00191381, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto1', 'par_load': True, 'threads': 8, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '111G', 'persist': 'persist-none', 'disable_snapshots': False}, [(4062340.0, 4062340.0, 0.00191927, 0.0, 0.0166665), (4070010.0, 4070010.0, 0.00191618, 0.0, 0.0), (4028190.0, 4028190.0, 0.00193597, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto2', 'par_load': True, 'threads': 8, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '111G', 'persist': 'persist-none', 'disable_snapshots': False}, [(4086490.0, 4086490.0, 0.00190773, 0.0, 0.0), (4080470.0, 4080470.0, 0.00191071, 0.0, 0.0), (4090320.0, 4090320.0, 0.00190628, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'kvdb', 'par_load': True, 'threads': 12, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '116G', 'persist': 'persist-none', 'disable_snapshots': False}, [(5110180.0, 5110180.0, 0.00229887, 0.0, 0.0), (5123020.0, 5123020.0, 0.00229284, 0.0, 0.0), (5133000.0, 5133000.0, 0.00228837, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto1', 'par_load': True, 'threads': 12, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '116G', 'persist': 'persist-none', 'disable_snapshots': False}, [(5664800.0, 5664800.0, 0.00206917, 0.0, 0.0), (5663160.0, 5663160.0, 0.00206965, 0.0, 0.0), (5682830.0, 5682830.0, 0.0020621, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto2', 'par_load': True, 'threads': 12, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '116G', 'persist': 'persist-none', 'disable_snapshots': False}, [(5718550.0, 5718550.0, 0.00204884, 0.0, 0.0), (5709720.0, 5709720.0, 0.00205213, 0.0, 0.0), (5699190.0, 5699190.0, 0.002056, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'kvdb', 'par_load': True, 'threads': 16, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '122G', 'persist': 'persist-none', 'disable_snapshots': False}, [(7173310.0, 7173310.0, 0.002181, 0.0, 0.0), (7176260.0, 7176260.0, 0.00218017, 0.0, 0.0), (7178980.0, 7178980.0, 0.00217931, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto1', 'par_load': True, 'threads': 16, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '122G', 'persist': 'persist-none', 'disable_snapshots': False}, [(7355090.0, 7355090.0, 0.0021259, 0.0, 0.0166666), (7342800.0, 7342800.0, 0.00212933, 0.0, 0.0), (7370260.0, 7370260.0, 0.00212146, 0.0, 0.0166665)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto2', 'par_load': True, 'threads': 16, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '122G', 'persist': 'persist-none', 'disable_snapshots': False}, [(7310960.0, 7310960.0, 0.00213415, 0.0, 0.0666662), (7429730.0, 7429730.0, 0.00210372, 0.0, 0.0), (7439340.0, 7439340.0, 0.002101, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'kvdb', 'par_load': True, 'threads': 20, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False}, [(7914540.0, 7914540.0, 0.00247778, 0.0, 0.0), (7873670.0, 7873670.0, 0.00249085, 0.0, 0.0), (7903400.0, 7903400.0, 0.00248125, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto1', 'par_load': True, 'threads': 20, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False}, [(8696710.0, 8696710.0, 0.00225026, 0.0, 0.0333329), (8874400.0, 8874400.0, 0.0022044, 0.0, 0.0), (8814210.0, 8814210.0, 0.00221945, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto2', 'par_load': True, 'threads': 20, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False}, [(9114630.0, 9114630.0, 0.00214478, 0.0, 0.0), (9120540.0, 9120540.0, 0.00214307, 0.0, 0.0166664), (9043650.0, 9043650.0, 0.00216185, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'kvdb', 'par_load': True, 'threads': 24, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '133G', 'persist': 'persist-none', 'disable_snapshots': False}, [(10138600.0, 10138600.0, 0.00231762, 0.0, 0.0), (10079000.0, 10079000.0, 0.0023317, 0.0, 0.0), (9903590.0, 9903590.0, 0.00237389, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto1', 'par_load': True, 'threads': 24, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '133G', 'persist': 'persist-none', 'disable_snapshots': False}, [(9765880.0, 9765880.0, 0.00240776, 0.0, 0.016665), (9783800.0, 9783800.0, 0.00240327, 0.0, 0.0499995), (9327830.0, 9327830.0, 0.0025234, 0.0, 0.0166651)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto2', 'par_load': True, 'threads': 24, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '133G', 'persist': 'persist-none', 'disable_snapshots': False}, [(10717100.0, 10717100.0, 0.00218978, 0.0, 0.0166665), (10722600.0, 10722600.0, 0.00218861, 0.0, 0.033333), (10716100.0, 10716100.0, 0.0021899, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'kvdb', 'par_load': True, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '139G', 'persist': 'persist-none', 'disable_snapshots': False}, [(11481300.0, 11481300.0, 0.00238929, 0.0, 0.0), (11399400.0, 11399400.0, 0.00240691, 0.0, 0.0), (11768400.0, 11768400.0, 0.00232986, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto1', 'par_load': True, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '139G', 'persist': 'persist-none', 'disable_snapshots': False}, [(9261960.0, 9261960.0, 0.00297322, 0.0, 0.0333329), (9226630.0, 9226630.0, 0.0029854, 0.0, 0.0166665), (9192360.0, 9192360.0, 0.00299657, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto2', 'par_load': True, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '139G', 'persist': 'persist-none', 'disable_snapshots': False}, [(12582400.0, 12582400.0, 0.00217553, 0.0, 0.0333329), (12499400.0, 12499400.0, 0.0021904, 0.0, 0.0499991), (12644200.0, 12644200.0, 0.00216484, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'kvdb', 'par_load': True, 'threads': 32, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '144G', 'persist': 'persist-none', 'disable_snapshots': False}, [(12645000.0, 12645000.0, 0.00248101, 0.0, 0.0), (13109200.0, 13109200.0, 0.0023914, 0.0, 0.0), (13058700.0, 13058700.0, 0.0024009, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto1', 'par_load': True, 'threads': 32, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '144G', 'persist': 'persist-none', 'disable_snapshots': False}, [(8865800.0, 8865800.0, 0.00355934, 0.0, 0.0166654), (8824900.0, 8824900.0, 0.00357681, 0.0, 0.0), (8834190.0, 8834190.0, 0.00357249, 0.0, 0.0166664)]), ({'disable_gc': False, 'scale_factor': 320000, 'db': 'ndb-proto2', 'par_load': True, 'threads': 32, 'log_compress': False, 'bench_opts': '--workload-mix 80,0,20,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_rmw', 'bench': 'ycsb', 'numa_memory': '144G', 'persist': 'persist-none', 'disable_snapshots': False}, [(14121400.0, 14121400.0, 0.00221659, 0.0, 0.0499989), (14173900.0, 14173900.0, 0.00220789, 0.0, 0.0166664), (14136400.0, 14136400.0, 0.00221364, 0.0, 0.0166661)]), ({'disable_gc': False, 'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': 'persist-real', 'disable_snapshots': False}, [(28225.8, 28225.8, 0.0353219, 85.0473, 0.0), (27996.8, 27996.8, 0.0355963, 81.6838, 0.0), (27607.9, 27607.9, 0.0361074, 82.335, 0.0)]), ({'disable_gc': False, 'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': 'persist-temp', 'disable_snapshots': False}, [(27926.2, 27926.2, 0.0356962, 90.5297, 0.0), (27510.4, 27510.4, 0.0362395, 87.2284, 0.0), (27872.8, 27872.8, 0.0357699, 86.3002, 0.0)]), ({'disable_gc': False, 'scale_factor': 1, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False}, [(29999.3, 29999.3, 0.0332548, 0.0, 0.0), (30322.2, 30322.2, 0.0328975, 0.0, 0.0), (30161.6, 30161.6, 0.0330703, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': 'persist-real', 'disable_snapshots': False}, [(100910.0, 100910.0, 0.0395189, 108.936, 4.16333), (100559.0, 100559.0, 0.0396542, 129.162, 4.1629), (100600.0, 100600.0, 0.0396374, 97.8462, 3.92957)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': 'persist-temp', 'disable_snapshots': False}, [(101538.0, 101538.0, 0.0392561, 108.948, 3.92817), (101177.0, 101177.0, 0.0394068, 103.502, 4.54524), (102354.0, 102354.0, 0.0389583, 106.842, 3.82977)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False}, [(109899.0, 109899.0, 0.036313, 0.0, 4.99994), (107896.0, 107896.0, 0.0369815, 0.0, 4.86663), (109172.0, 109172.0, 0.0365549, 0.0, 4.61663)]), ({'disable_gc': False, 'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': 'persist-real', 'disable_snapshots': False}, [(189826.0, 189826.0, 0.042021, 125.662, 7.76003), (185841.0, 185841.0, 0.0429076, 142.749, 7.2577), (188559.0, 188559.0, 0.0422831, 156.151, 7.62363)]), ({'disable_gc': False, 'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': 'persist-temp', 'disable_snapshots': False}, [(179857.0, 179857.0, 0.0428203, 1943.32, 7.73359), (179443.0, 179443.0, 0.0427535, 2660.23, 7.41542), (179219.0, 179219.0, 0.0428436, 2620.55, 7.51875)]), ({'disable_gc': False, 'scale_factor': 8, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False}, [(205444.0, 205444.0, 0.0388523, 0.0, 9.24991), (205071.0, 205071.0, 0.0389223, 0.0, 9.49993), (205272.0, 205272.0, 0.0388865, 0.0, 8.86657)]), ({'disable_gc': False, 'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': 'persist-real', 'disable_snapshots': False}, [(275112.0, 275112.0, 0.0434859, 188.465, 10.0563), (274888.0, 274888.0, 0.0435201, 136.944, 10.7392), (277746.0, 277746.0, 0.0430842, 155.892, 11.5585)]), ({'disable_gc': False, 'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': 'persist-temp', 'disable_snapshots': False}, [(184218.0, 184218.0, 0.0604978, 7802.75, 5.94474), (186948.0, 186948.0, 0.0592447, 8218.08, 6.06701), (186407.0, 186407.0, 0.0595752, 7497.37, 7.06313)]), ({'disable_gc': False, 'scale_factor': 12, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False}, [(302915.0, 302915.0, 0.0395281, 0.0, 13.2995), (301651.0, 301651.0, 0.039695, 0.0, 13.8499), (300453.0, 300453.0, 0.0398527, 0.0, 13.2489)]), ({'disable_gc': False, 'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': 'persist-real', 'disable_snapshots': False}, [(371737.0, 371737.0, 0.0428917, 247.802, 14.2299), (364991.0, 364991.0, 0.0437131, 293.695, 14.8887), (370023.0, 370023.0, 0.0431254, 163.949, 15.4243)]), ({'disable_gc': False, 'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': 'persist-temp', 'disable_snapshots': False}, [(187739.0, 187739.0, 0.0723913, 11632.9, 6.8071), (186315.0, 186315.0, 0.0792256, 10270.6, 6.54093), (187679.0, 187679.0, 0.0746198, 9838.45, 5.98721)]), ({'disable_gc': False, 'scale_factor': 16, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False}, [(401610.0, 401610.0, 0.0397526, 0.0, 17.1163), (402042.0, 402042.0, 0.0397092, 0.0, 17.4998), (399975.0, 399975.0, 0.0399107, 0.0, 17.63)]), ({'disable_gc': False, 'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': 'persist-real', 'disable_snapshots': False}, [(444169.0, 444169.0, 0.0448953, 151.388, 17.449), (444065.0, 444065.0, 0.0449003, 148.806, 18.0961), (440532.0, 440532.0, 0.0449769, 169.238, 18.0831)]), ({'disable_gc': False, 'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': 'persist-temp', 'disable_snapshots': False}, [(188552.0, 188552.0, 0.0946188, 11424.7, 6.79256), (185737.0, 185737.0, 0.0834558, 22108.5, 6.27152), (187872.0, 187872.0, 0.0922701, 11929.8, 6.62238)]), ({'disable_gc': False, 'scale_factor': 20, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False}, [(485609.0, 485609.0, 0.0410983, 0.0, 20.483), (488427.0, 488427.0, 0.0408604, 0.0, 20.4812), (488673.0, 488673.0, 0.0408392, 0.0, 21.4497)]), ({'disable_gc': False, 'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': 'persist-real', 'disable_snapshots': False}, [(531895.0, 531895.0, 0.0449565, 392.421, 20.8648), (519911.0, 519911.0, 0.0453662, 427.594, 20.9243), (532396.0, 532396.0, 0.0449219, 414.072, 20.3021)]), ({'disable_gc': False, 'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': 'persist-temp', 'disable_snapshots': False}, [(189167.0, 189167.0, 0.0995511, 25374.6, 6.19028), (190403.0, 190403.0, 0.113274, 30230.3, 7.28327), (191628.0, 191628.0, 0.112405, 28540.7, 6.66101)]), ({'disable_gc': False, 'scale_factor': 24, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False}, [(578584.0, 578584.0, 0.0413895, 0.0, 24.89), (576649.0, 576649.0, 0.0415336, 0.0, 25.0663), (573606.0, 573606.0, 0.0417509, 0.0, 24.8613)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False}, [(614343.0, 614343.0, 0.0454167, 128.291, 24.9615), (614416.0, 614416.0, 0.045434, 129.705, 24.4744), (610425.0, 610425.0, 0.0457182, 119.286, 25.4812)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-temp', 'disable_snapshots': False}, [(194751.0, 194751.0, 0.118667, 26223.6, 6.37576), (192813.0, 192813.0, 0.112795, 34142.6, 6.37018), (190514.0, 190514.0, 0.113389, 36029.3, 5.79154)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(660947.0, 660947.0, 0.0422742, 0.0, 27.3494), (672976.0, 672976.0, 0.0415164, 0.0, 28.3322), (668866.0, 668866.0, 0.0417719, 0.0, 26.9708)]), ({'disable_gc': False, 'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': 'persist-real', 'disable_snapshots': False}, [(677187.0, 677187.0, 0.0468518, 985.0, 26.8883), (675340.0, 675340.0, 0.04693, 1063.74, 27.0104), (667633.0, 667633.0, 0.0473975, 946.623, 26.3616)]), ({'disable_gc': False, 'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': 'persist-temp', 'disable_snapshots': False}, [(190389.0, 190389.0, 0.133702, 59431.5, 6.21439), (189099.0, 189099.0, 0.139139, 59091.3, 6.38332), (188621.0, 188621.0, 0.138977, 55855.2, 6.12984)]), ({'disable_gc': False, 'scale_factor': 32, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'log_compress': False, 'bench_opts': '', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'scale_tpcc', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False}, [(799555.0, 799555.0, 0.0399245, 0.0, 35.9474), (789040.0, 789040.0, 0.0404528, 0.0, 36.0426), (802551.0, 802551.0, 0.0397774, 0.0, 35.266)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(744818.0, 744818.0, 0.0375421, 0.0, 0.0), (729245.0, 729245.0, 0.0383431, 0.0, 0.0), (744176.0, 744176.0, 0.0375721, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(722201.0, 722201.0, 0.0387092, 0.0, 109.097), (725699.0, 725699.0, 0.0385213, 0.0, 109.148), (731531.0, 731531.0, 0.0382124, 0.0, 112.411)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(718327.0, 718327.0, 0.0389068, 0.0, 215.074), (724478.0, 724478.0, 0.0385769, 0.0, 218.096), (723477.0, 723477.0, 0.0386306, 0.0, 217.329)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(720978.0, 720978.0, 0.0387546, 0.0, 321.378), (717441.0, 717441.0, 0.038947, 0.0, 326.11), (716146.0, 716146.0, 0.0390174, 0.0, 322.794)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(710948.0, 710948.0, 0.039292, 0.0, 426.372), (711679.0, 711679.0, 0.0392517, 0.0, 429.325), (712859.0, 712859.0, 0.0391863, 0.0, 419.673)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(716067.0, 716067.0, 0.0390019, 0.0, 537.117), (709727.0, 709727.0, 0.0393509, 0.0, 524.4), (712776.0, 712776.0, 0.0391784, 0.0, 532.62)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(698942.0, 698942.0, 0.03994, 0.0, 624.089), (707212.0, 707212.0, 0.0394828, 0.0, 622.304), (706480.0, 706480.0, 0.0395227, 0.0, 626.878)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(713453.0, 713453.0, 0.0391257, 0.0, 737.55), (698711.0, 698711.0, 0.0399528, 0.0, 716.737), (707546.0, 707546.0, 0.0394551, 0.0, 733.136)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(686513.0, 686513.0, 0.0406552, 0.0, 795.383), (701446.0, 701446.0, 0.0397873, 0.0, 817.033), (696570.0, 696570.0, 0.0400673, 0.0, 817.334)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(704394.0, 704394.0, 0.0396135, 0.0, 920.414), (684835.0, 684835.0, 0.0407456, 0.0, 901.231), (703963.0, 703963.0, 0.0396379, 0.0, 926.467)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(698642.0, 698642.0, 0.0399317, 0.0, 1015.81), (704281.0, 704281.0, 0.0396104, 0.0, 1014.66), (705923.0, 705923.0, 0.0395182, 0.0, 1027.02)])] + [({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(745168.0, 745168.0, 0.0375228, 0.0, 0.0), (718617.0, 718617.0, 0.0389127, 0.0, 0.0), (734465.0, 734465.0, 0.0380727, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(718176.0, 718176.0, 0.0389247, 0.0, 107.765), (732056.0, 732056.0, 0.038187, 0.0, 111.181), (725086.0, 725086.0, 0.0385547, 0.0, 112.148)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(718581.0, 718581.0, 0.0388926, 0.0, 216.729), (720240.0, 720240.0, 0.0388036, 0.0, 219.128), (724160.0, 724160.0, 0.0385882, 0.0, 219.179)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(723001.0, 723001.0, 0.0386457, 0.0, 323.577), (716546.0, 716546.0, 0.038994, 0.0, 321.71), (715844.0, 715844.0, 0.0390336, 0.0, 323.227)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(718339.0, 718339.0, 0.0388866, 0.0, 430.723), (718861.0, 718861.0, 0.0388588, 0.0, 430.825), (701943.0, 701943.0, 0.0397957, 0.0, 419.025)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(713035.0, 713035.0, 0.0391695, 0.0, 521.974), (714125.0, 714125.0, 0.0391062, 0.0, 528.383), (702524.0, 702524.0, 0.0397539, 0.0, 521.807)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(705886.0, 705886.0, 0.0395548, 0.0, 630.086), (712365.0, 712365.0, 0.0391934, 0.0, 634.904), (705125.0, 705125.0, 0.0395977, 0.0, 628.239)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(708353.0, 708353.0, 0.0394067, 0.0, 728.587), (703863.0, 703863.0, 0.0396599, 0.0, 733.37), (701791.0, 701791.0, 0.039778, 0.0, 726.171)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(696696.0, 696696.0, 0.0400605, 0.0, 811.101), (698641.0, 698641.0, 0.0399471, 0.0, 812.769), (700486.0, 700486.0, 0.0398438, 0.0, 818.919)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(698854.0, 698854.0, 0.0399254, 0.0, 918.434), (694037.0, 694037.0, 0.0402035, 0.0, 910.21), (699151.0, 699151.0, 0.039914, 0.0, 905.744)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(694882.0, 694882.0, 0.0401454, 0.0, 1013.35), (702246.0, 702246.0, 0.0397262, 0.0, 1012.1), (698722.0, 698722.0, 0.039925, 0.0, 1008.45)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(880123.0, 880123.0, 0.0317634, 0.0, 0.0), (882710.0, 882710.0, 0.03167, 0.0, 0.0), (881295.0, 881295.0, 0.0317214, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(853692.0, 853692.0, 0.0327402, 0.0, 125.698), (857504.0, 857504.0, 0.0325954, 0.0, 123.048), (869947.0, 869947.0, 0.032129, 0.0, 125.998)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(868889.0, 868889.0, 0.0321596, 0.0, 249.879), (857076.0, 857076.0, 0.0326019, 0.0, 252.13), (851537.0, 851537.0, 0.0328151, 0.0, 253.88)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(855104.0, 855104.0, 0.0326696, 0.0, 372.811), (843555.0, 843555.0, 0.0331169, 0.0, 366.711), (855979.0, 855979.0, 0.032636, 0.0, 377.682)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(854892.0, 854892.0, 0.0326699, 0.0, 501.168), (848600.0, 848600.0, 0.0329129, 0.0, 506.259), (833309.0, 833309.0, 0.0335183, 0.0, 492.258)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(842414.0, 842414.0, 0.033148, 0.0, 614.09), (839501.0, 839501.0, 0.0332639, 0.0, 607.718), (853846.0, 853846.0, 0.032703, 0.0, 626.258)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(835522.0, 835522.0, 0.0334136, 0.0, 719.842), (839114.0, 839114.0, 0.0332686, 0.0, 733.639), (837311.0, 837311.0, 0.0333425, 0.0, 729.656)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(837669.0, 837669.0, 0.0333211, 0.0, 846.921), (825053.0, 825053.0, 0.0338307, 0.0, 829.035), (840314.0, 840314.0, 0.0332143, 0.0, 846.671)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(832251.0, 832251.0, 0.0335296, 0.0, 956.086), (821790.0, 821790.0, 0.0339582, 0.0, 941.503), (816758.0, 816758.0, 0.0341685, 0.0, 935.368)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(839997.0, 839997.0, 0.0332133, 0.0, 1078.94), (829126.0, 829126.0, 0.0336504, 0.0, 1064.35), (824268.0, 824268.0, 0.0338495, 0.0, 1068.24)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(823633.0, 823633.0, 0.0338678, 0.0, 1167.56), (827944.0, 827944.0, 0.0336917, 0.0, 1165.97), (817099.0, 817099.0, 0.034139, 0.0, 1164.33)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(1236310.0, 1236310.0, 0.0226018, 0.0, 0.0), (1244240.0, 1244240.0, 0.0224582, 0.0, 0.0), (1245850.0, 1245850.0, 0.0224283, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(1045020.0, 1045020.0, 0.026748, 0.0, 0.0), (1036090.0, 1036090.0, 0.0269785, 0.0, 0.0), (1039220.0, 1039220.0, 0.0268971, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(865856.0, 865856.0, 0.0322923, 0.0, 0.0), (861881.0, 861881.0, 0.0324414, 0.0, 0.0), (844881.0, 844881.0, 0.0330947, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(705044.0, 705044.0, 0.0396666, 0.0, 0.0), (703775.0, 703775.0, 0.0397393, 0.0, 0.0), (708846.0, 708846.0, 0.0394545, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(591404.0, 591404.0, 0.0472985, 0.0, 0.0), (584303.0, 584303.0, 0.0478737, 0.0, 0.0), (575244.0, 575244.0, 0.048627, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(484320.0, 484320.0, 0.0577639, 0.0, 0.0), (494191.0, 494191.0, 0.0566106, 0.0, 0.0), (495974.0, 495974.0, 0.0564078, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(419854.0, 419854.0, 0.0666428, 0.0, 0.0), (413765.0, 413765.0, 0.0676235, 0.0, 0.0), (415404.0, 415404.0, 0.0673568, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(358433.0, 358433.0, 0.0780522, 0.0, 0.0), (358356.0, 358356.0, 0.0780874, 0.0, 0.0), (361831.0, 361831.0, 0.0773367, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(318298.0, 318298.0, 0.0879168, 0.0, 0.0), (316568.0, 316568.0, 0.0883999, 0.0, 0.0), (317404.0, 317404.0, 0.0881668, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(280795.0, 280795.0, 0.0996671, 0.0, 0.0), (280361.0, 280361.0, 0.0998229, 0.0, 0.0), (282226.0, 282226.0, 0.0991631, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 28, 'db': 'kvdb-st', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:pct', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(256166.0, 256166.0, 0.109255, 0.0, 0.0), (252953.0, 252953.0, 0.110645, 0.0, 0.0), (255144.0, 255144.0, 0.109693, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'kvdb-st', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False}, [(43855.1, 43855.1, 0.0227563, 0.0, 0.0), (43888.4, 43888.4, 0.0227407, 0.0, 0.0), (44572.7, 44572.7, 0.0223913, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False}, [(30605.7, 30605.7, 0.0326234, 0.0, 0.0), (31320.7, 31320.7, 0.0318794, 0.0, 0.0), (31041.7, 31041.7, 0.0321648, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False}, [(31066.6, 31066.6, 0.0321422, 0.0, 0.0), (30482.7, 30482.7, 0.032754, 0.0, 0.0), (30725.5, 30725.5, 0.0324976, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False}, [(31560.3, 31560.3, 0.0316371, 0.0, 0.0), (30490.8, 30490.8, 0.0327463, 0.0, 0.0), (31364.5, 31364.5, 0.0318347, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 1, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False}, [(32179.5, 32179.5, 0.0310248, 0.0, 0.0), (31418.1, 31418.1, 0.0317786, 0.0, 0.0), (31469.4, 31469.4, 0.0317263, 0.0, 0.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 2, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '8G', 'persist': 'persist-none', 'disable_snapshots': False}, [(62847.1, 62847.1, 0.0317729, 0.0, 3.08331), (60534.9, 60534.9, 0.0329676, 0.0, 2.93331), (62151.0, 62151.0, 0.0321257, 0.0, 2.78332)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 2, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '8G', 'persist': 'persist-none', 'disable_snapshots': False}, [(61213.8, 61213.8, 0.03262, 0.0, 3.26664), (61263.6, 61263.6, 0.0325946, 0.0, 3.21665), (62314.0, 62314.0, 0.0320424, 0.0, 2.99998)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 2, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '8G', 'persist': 'persist-none', 'disable_snapshots': False}, [(61973.1, 61973.1, 0.0322061, 0.0, 3.03332), (62583.3, 62583.3, 0.0319036, 0.0, 3.08332), (61742.0, 61742.0, 0.0323412, 0.0, 3.19998)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 2, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '8G', 'persist': 'persist-none', 'disable_snapshots': False}, [(63072.2, 63072.2, 0.0316574, 0.0, 3.61665), (63271.7, 63271.7, 0.0315533, 0.0, 2.91665), (63570.4, 63570.4, 0.0314084, 0.0, 2.99999)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False}, [(118405.0, 118405.0, 0.0337239, 0.0, 18.0832), (117069.0, 117069.0, 0.0341063, 0.0, 18.5832), (117203.0, 117203.0, 0.0340672, 0.0, 18.5832)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False}, [(118274.0, 118274.0, 0.0337591, 0.0, 17.9999), (117501.0, 117501.0, 0.0339807, 0.0, 17.1999), (119067.0, 119067.0, 0.0335348, 0.0, 17.7832)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False}, [(116340.0, 116340.0, 0.0343207, 0.0, 16.7665), (116166.0, 116166.0, 0.0343719, 0.0, 17.6332), (118098.0, 118098.0, 0.033811, 0.0, 17.8332)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 4, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False}, [(119795.0, 119795.0, 0.0333284, 0.0, 18.3165), (119479.0, 119479.0, 0.0334195, 0.0, 18.7832), (120864.0, 120864.0, 0.0330335, 0.0, 19.1998)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 6, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '24G', 'persist': 'persist-none', 'disable_snapshots': False}, [(156516.0, 156516.0, 0.0356539, 0.0, 9296.25), (160001.0, 160001.0, 0.0348753, 0.0, 9580.05), (159009.0, 159009.0, 0.0351108, 0.0, 9510.0)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 6, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '24G', 'persist': 'persist-none', 'disable_snapshots': False}, [(158802.0, 158802.0, 0.0351452, 0.0, 9504.12), (159279.0, 159279.0, 0.0350251, 0.0, 9550.32), (154746.0, 154746.0, 0.0360781, 0.0, 9252.64)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 6, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '24G', 'persist': 'persist-none', 'disable_snapshots': False}, [(158237.0, 158237.0, 0.0352603, 0.0, 9476.15), (159893.0, 159893.0, 0.0348924, 0.0, 9519.93), (158347.0, 158347.0, 0.0352528, 0.0, 9471.68)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 6, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '24G', 'persist': 'persist-none', 'disable_snapshots': False}, [(170417.0, 170417.0, 0.034849, 0.0, 871.275), (172535.0, 172535.0, 0.0344206, 0.0, 881.622), (170994.0, 170994.0, 0.0347308, 0.0, 876.925)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False}, [(196955.0, 196955.0, 0.0364097, 0.0, 18501.5), (198523.0, 198523.0, 0.0361219, 0.0, 18628.1), (197126.0, 197126.0, 0.0363815, 0.0, 18481.3)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False}, [(198100.0, 198100.0, 0.0362044, 0.0, 18554.2), (199049.0, 199049.0, 0.0360214, 0.0, 18700.2), (201121.0, 201121.0, 0.0356409, 0.0, 18909.3)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False}, [(198105.0, 198105.0, 0.0362002, 0.0, 18588.4), (198277.0, 198277.0, 0.0361677, 0.0, 18605.8), (199991.0, 199991.0, 0.0358402, 0.0, 18767.6)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 8, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False}, [(224949.0, 224949.0, 0.0350481, 0.0, 1744.53), (221430.0, 221430.0, 0.0356075, 0.0, 1730.5), (220522.0, 220522.0, 0.0357496, 0.0, 1721.03)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 10, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '40G', 'persist': 'persist-none', 'disable_snapshots': False}, [(176106.0, 176106.0, 0.0472443, 0.0, 27087.2), (179962.0, 179962.0, 0.0463343, 0.0, 27664.6), (176850.0, 176850.0, 0.047126, 0.0, 27107.9)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 10, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '40G', 'persist': 'persist-none', 'disable_snapshots': False}, [(176422.0, 176422.0, 0.0471608, 0.0, 27095.6), (179979.0, 179979.0, 0.0463294, 0.0, 27597.4), (178972.0, 178972.0, 0.0466128, 0.0, 27417.4)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 10, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '40G', 'persist': 'persist-none', 'disable_snapshots': False}, [(180200.0, 180200.0, 0.046302, 0.0, 27573.9), (176131.0, 176131.0, 0.0472636, 0.0, 27066.9), (177472.0, 177472.0, 0.0468588, 0.0, 27390.7)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 10, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '40G', 'persist': 'persist-none', 'disable_snapshots': False}, [(209124.0, 209124.0, 0.0466375, 0.0, 2680.76), (209849.0, 209849.0, 0.0464893, 0.0, 2666.57), (205378.0, 205378.0, 0.0474777, 0.0, 2636.59)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False}, [(139588.0, 139588.0, 0.0659393, 0.0, 28819.5), (140095.0, 140095.0, 0.0657099, 0.0, 28952.9), (139966.0, 139966.0, 0.0656314, 0.0, 28976.9)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False}, [(140820.0, 140820.0, 0.0653365, 0.0, 29218.2), (136922.0, 136922.0, 0.0669741, 0.0, 28417.5), (137839.0, 137839.0, 0.0665611, 0.0, 28531.6)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False}, [(140798.0, 140798.0, 0.0654683, 0.0, 29213.5), (137944.0, 137944.0, 0.0665734, 0.0, 28517.1), (138015.0, 138015.0, 0.0664668, 0.0, 28568.3)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 12, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False}, [(171605.0, 171605.0, 0.0677162, 0.0, 2794.55), (169985.0, 169985.0, 0.0683708, 0.0, 2778.57), (170709.0, 170709.0, 0.0680968, 0.0, 2803.46)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False}, [(115870.0, 115870.0, 0.0937046, 0.0, 37720.7), (114680.0, 114680.0, 0.0946853, 0.0, 37366.4), (117800.0, 117800.0, 0.0926635, 0.0, 38320.1)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False}, [(116384.0, 116384.0, 0.0934684, 0.0, 37934.7), (115993.0, 115993.0, 0.0936669, 0.0, 37713.0), (114983.0, 114983.0, 0.0943386, 0.0, 37406.5)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False}, [(114252.0, 114252.0, 0.094901, 0.0, 37231.7), (116929.0, 116929.0, 0.093139, 0.0, 38047.8), (114836.0, 114836.0, 0.0945289, 0.0, 37429.7)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 16, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False}, [(155493.0, 155493.0, 0.0981295, 0.0, 3883.07), (155426.0, 155426.0, 0.098211, 0.0, 3876.51), (153428.0, 153428.0, 0.0994932, 0.0, 3832.03)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False}, [(60475.2, 60475.2, 0.182445, 0.0, 29488.2), (60107.8, 60107.8, 0.182948, 0.0, 29348.9), (61280.1, 61280.1, 0.18006, 0.0, 29705.9)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False}, [(60457.2, 60457.2, 0.182212, 0.0, 29363.5), (59668.9, 59668.9, 0.184091, 0.0, 29139.5), (60189.9, 60189.9, 0.184746, 0.0, 29619.2)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False}, [(60690.5, 60690.5, 0.181272, 0.0, 29316.2), (60653.1, 60653.1, 0.18253, 0.0, 29726.2), (60324.7, 60324.7, 0.182389, 0.0, 29379.7)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 20, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False}, [(93175.0, 93175.0, 0.199149, 0.0, 3175.85), (93062.2, 93062.2, 0.199089, 0.0, 3126.64), (92847.4, 92847.4, 0.199474, 0.0, 3119.26)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False}, [(49744.9, 49744.9, 0.241377, 0.0, 31513.3), (47512.1, 47512.1, 0.254183, 0.0, 30747.1), (50122.0, 50122.0, 0.241101, 0.0, 32188.8)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False}, [(49325.6, 49325.6, 0.24355, 0.0, 31058.0), (50315.1, 50315.1, 0.240308, 0.0, 32304.2), (49780.8, 49780.8, 0.242475, 0.0, 32178.8)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False}, [(49702.4, 49702.4, 0.241583, 0.0, 31700.6), (50319.8, 50319.8, 0.23972, 0.0, 32107.6), (49576.4, 49576.4, 0.242742, 0.0, 31283.6)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 24, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False}, [(84953.5, 84953.5, 0.259721, 0.0, 3600.62), (84818.5, 84818.5, 0.260264, 0.0, 3643.95), (84363.5, 84363.5, 0.26163, 0.0, 3578.91)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(28735.5, 28735.5, 0.416581, 0.0, 23273.6), (29008.3, 29008.3, 0.419257, 0.0, 23993.0), (29544.0, 29544.0, 0.407269, 0.0, 24158.4)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(28835.9, 28835.9, 0.415777, 0.0, 23612.4), (29283.3, 29283.3, 0.412591, 0.0, 24090.3), (29287.1, 29287.1, 0.406696, 0.0, 23664.6)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(29758.3, 29758.3, 0.409433, 0.0, 24554.2), (28236.7, 28236.7, 0.419801, 0.0, 22938.4), (29144.5, 29144.5, 0.411853, 0.0, 23565.5)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 28, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False}, [(55879.1, 55879.1, 0.447132, 0.0, 2865.33), (54264.9, 54264.9, 0.462334, 0.0, 2796.55), (56233.5, 56233.5, 0.446034, 0.0, 2898.39)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False}, [(23768.9, 23768.9, 0.537222, 0.0, 23606.3), (24678.7, 24678.7, 0.512726, 0.0, 24112.1), (25735.6, 25735.6, 0.499694, 0.0, 25295.3)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False}, [(25904.7, 25904.7, 0.495937, 0.0, 25535.5), (25071.9, 25071.9, 0.510231, 0.0, 24728.7), (25014.4, 25014.4, 0.511266, 0.0, 24456.1)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False}, [(25331.1, 25331.1, 0.503081, 0.0, 24647.1), (25277.4, 25277.4, 0.507604, 0.0, 24967.8), (24860.0, 24860.0, 0.514896, 0.0, 24465.6)]), ({'disable_gc': False, 'scale_factor': 4, 'db': 'ndb-proto2', 'par_load': False, 'threads': 32, 'log_compress': False, 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'log_fake_writes': False, 'retry': False, 'log_nofsync': False, 'name': 'multipart:skew', 'bench': 'tpcc', 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False}, [(51318.9, 51318.9, 0.553087, 0.0, 3080.28), (51523.8, 51523.8, 0.550038, 0.0, 3105.77), (51377.4, 51377.4, 0.551486, 0.0, 3071.04)])]
diff --git a/silo/benchmarks/results/istc3-8-21-13_cameraready-1.py b/silo/benchmarks/results/istc3-8-21-13_cameraready-1.py
new file mode 100644 (file)
index 0000000..8ac8269
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(575616.0, 575616.0, 0.00168949, 0.0, 0.0), (578536.0, 578536.0, 0.00168159, 0.0, 0.0), (580221.0, 580221.0, 0.00167668, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(558921.0, 558921.0, 0.00174234, 0.0, 0.0), (556947.0, 556947.0, 0.00174774, 0.0, 0.0), (553586.0, 553586.0, 0.00175766, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(560097.0, 560097.0, 0.00173837, 0.0, 0.0), (563833.0, 563833.0, 0.00172632, 0.0, 0.0), (557317.0, 557317.0, 0.00174698, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(2395410.0, 2395410.0, 0.00162179, 0.0, 0.0), (2412890.0, 2412890.0, 0.00160991, 0.0, 0.0), (2414120.0, 2414120.0, 0.00160909, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(2248410.0, 2248410.0, 0.00173062, 0.0, 0.0), (2256550.0, 2256550.0, 0.0017243, 0.0, 0.0), (2263440.0, 2263440.0, 0.00171894, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(2257040.0, 2257040.0, 0.00172376, 0.0, 0.0), (2262650.0, 2262650.0, 0.00171955, 0.0, 0.0), (2261210.0, 2261210.0, 0.00172048, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(4597760.0, 4597760.0, 0.00169162, 0.0, 0.0), (4616010.0, 4616010.0, 0.00168483, 0.0, 0.0), (4608240.0, 4608240.0, 0.00168781, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(4314570.0, 4314570.0, 0.00180563, 0.0, 0.0166665), (4333450.0, 4333450.0, 0.00179756, 0.0, 0.0), (4331200.0, 4331200.0, 0.00179853, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(4348940.0, 4348940.0, 0.00179096, 0.0, 0.0), (4329300.0, 4329300.0, 0.00179932, 0.0, 0.0), (4334450.0, 4334450.0, 0.00179716, 0.0, 0.0166665)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(6367420.0, 6367420.0, 0.00183657, 0.0, 0.0), (6293770.0, 6293770.0, 0.00185356, 0.0, 0.0), (6342450.0, 6342450.0, 0.00184406, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(5999830.0, 5999830.0, 0.00195169, 0.0, 0.0), (6001700.0, 6001700.0, 0.00195111, 0.0, 0.0166666), (5999440.0, 5999440.0, 0.00195188, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(6045620.0, 6045620.0, 0.00193648, 0.0, 0.0), (6064630.0, 6064630.0, 0.00193036, 0.0, 0.0166665), (6040860.0, 6040860.0, 0.00193817, 0.0, 0.0166666)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(8307300.0, 8307300.0, 0.001878, 0.0, 0.0), (8297700.0, 8297700.0, 0.00188009, 0.0, 0.0), (8280920.0, 8280920.0, 0.00188402, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(7817290.0, 7817290.0, 0.00199833, 0.0, 0.0166665), (7802520.0, 7802520.0, 0.00200201, 0.0, 0.0), (7811460.0, 7811460.0, 0.00199984, 0.0, 0.0833322)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(7928420.0, 7928420.0, 0.00196961, 0.0, 0.0166666), (7919210.0, 7919210.0, 0.00197197, 0.0, 0.0166665), (7917390.0, 7917390.0, 0.00197234, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(10267500.0, 10267500.0, 0.00189997, 0.0, 0.0), (10270900.0, 10270900.0, 0.00189931, 0.0, 0.0), (10265500.0, 10265500.0, 0.00190017, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(9449810.0, 9449810.0, 0.00206788, 0.0, 0.0333329), (9448340.0, 9448340.0, 0.00206841, 0.0, 0.0), (9440770.0, 9440770.0, 0.00207008, 0.0, 0.0166664)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(9799650.0, 9799650.0, 0.00199244, 0.0, 0.0166665), (9806870.0, 9806870.0, 0.00199089, 0.0, 0.0), (9789250.0, 9789250.0, 0.0019947, 0.0, 0.066666)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(12162600.0, 12162600.0, 0.00192521, 0.0, 0.0), (12172800.0, 12172800.0, 0.00192338, 0.0, 0.0), (12166500.0, 12166500.0, 0.00192455, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(10423900.0, 10423900.0, 0.00225399, 0.0, 0.0499992), (10411800.0, 10411800.0, 0.00225661, 0.0, 0.0166664), (10389100.0, 10389100.0, 0.00226165, 0.0, 0.0499992)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(11600200.0, 11600200.0, 0.0020204, 0.0, 0.0), (11551200.0, 11551200.0, 0.00202914, 0.0, 0.0499993), (11572700.0, 11572700.0, 0.0020253, 0.0, 0.0499994)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(14168000.0, 14168000.0, 0.00192825, 0.0, 0.0), (14211200.0, 14211200.0, 0.00192234, 0.0, 0.0), (14190700.0, 14190700.0, 0.00192516, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(9381700.0, 9381700.0, 0.00293617, 0.0, 0.0166662), (9488990.0, 9488990.0, 0.00290237, 0.0, 0.0666645), (9500500.0, 9500500.0, 0.00289888, 0.0, 0.0499995)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(13522500.0, 13522500.0, 0.00202218, 0.0, 0.0333326), (13534900.0, 13534900.0, 0.00202034, 0.0, 0.0333327), (13306400.0, 13306400.0, 0.00205582, 0.0, 0.0333326)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(15798400.0, 15798400.0, 0.00197723, 0.0, 0.0), (15856800.0, 15856800.0, 0.00196982, 0.0, 0.0), (15840300.0, 15840300.0, 0.00197191, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(8744460.0, 8744460.0, 0.00361026, 0.0, 0.0166632), (8731700.0, 8731700.0, 0.00361618, 0.0, 0.0), (8736010.0, 8736010.0, 0.00361437, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(15105200.0, 15105200.0, 0.00206949, 0.0, 0.0833162), (15055600.0, 15055600.0, 0.00207672, 0.0, 0.116665), (15043800.0, 15043800.0, 0.00207845, 0.0, 0.0833309)])]
diff --git a/silo/benchmarks/results/istc3-8-21-13_cameraready.py b/silo/benchmarks/results/istc3-8-21-13_cameraready.py
new file mode 100644 (file)
index 0000000..002bd69
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '101G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(525615.0, 525615.0, 0.00185516, 0.0, 0.0), (515410.0, 515410.0, 0.00189345, 0.0, 0.0), (520312.0, 520312.0, 0.00187489, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '101G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(497195.0, 497195.0, 0.00196389, 0.0, 0.0), (491779.0, 491779.0, 0.00198601, 0.0, 0.0), (494453.0, 494453.0, 0.00197536, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '101G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(495989.0, 495989.0, 0.00196928, 0.0, 0.0), (496430.0, 496430.0, 0.00196703, 0.0, 0.0), (496666.0, 496666.0, 0.0019662, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '105G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(2169280.0, 2169280.0, 0.00179469, 0.0, 0.0), (2157320.0, 2157320.0, 0.00180592, 0.0, 0.0), (2157930.0, 2157930.0, 0.00180571, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '105G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(2005710.0, 2005710.0, 0.00194654, 0.0, 0.0166666), (2006780.0, 2006780.0, 0.00194519, 0.0, 0.0), (2002000.0, 2002000.0, 0.00195016, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '105G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(1977830.0, 1977830.0, 0.00197388, 0.0, 0.0), (2010380.0, 2010380.0, 0.00194187, 0.0, 0.0), (1992090.0, 1992090.0, 0.0019602, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '111G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(4232030.0, 4232030.0, 0.00184175, 0.0, 0.0), (4240500.0, 4240500.0, 0.00183809, 0.0, 0.0), (4221930.0, 4221930.0, 0.00184649, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '111G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(3924390.0, 3924390.0, 0.0019905, 0.0, 0.0), (3930880.0, 3930880.0, 0.00198678, 0.0, 0.0), (3935780.0, 3935780.0, 0.00198435, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '111G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(3958040.0, 3958040.0, 0.00197312, 0.0, 0.0), (3888130.0, 3888130.0, 0.00200616, 0.0, 0.0166665), (3960540.0, 3960540.0, 0.00197191, 0.0, 0.0166665)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '116G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(5956440.0, 5956440.0, 0.00196661, 0.0, 0.0), (5930870.0, 5930870.0, 0.0019753, 0.0, 0.0), (5963950.0, 5963950.0, 0.00196407, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '116G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(5514320.0, 5514320.0, 0.00212806, 0.0, 0.0), (5624140.0, 5624140.0, 0.00208577, 0.0, 0.0), (5637120.0, 5637120.0, 0.00208013, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '116G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(5689030.0, 5689030.0, 0.0020614, 0.0, 0.0), (5701160.0, 5701160.0, 0.00205693, 0.0, 0.0), (5690470.0, 5690470.0, 0.0020607, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '122G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(7731590.0, 7731590.0, 0.00202149, 0.0, 0.0), (7724800.0, 7724800.0, 0.00202305, 0.0, 0.0), (7739500.0, 7739500.0, 0.00201935, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '122G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(7318690.0, 7318690.0, 0.00213825, 0.0, 0.0166666), (7332020.0, 7332020.0, 0.00213407, 0.0, 0.0), (7324860.0, 7324860.0, 0.00213609, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '122G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(7411250.0, 7411250.0, 0.00211038, 0.0, 0.0166665), (7421290.0, 7421290.0, 0.00210778, 0.0, 0.0), (7406560.0, 7406560.0, 0.00211225, 0.0, 0.0333329)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '128G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(9489010.0, 9489010.0, 0.00205971, 0.0, 0.0), (9434600.0, 9434600.0, 0.00207199, 0.0, 0.0), (9479070.0, 9479070.0, 0.00206207, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '128G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(8842230.0, 8842230.0, 0.00221404, 0.0, 0.0166664), (8831230.0, 8831230.0, 0.00221663, 0.0, 0.0), (8828560.0, 8828560.0, 0.00221729, 0.0, 0.0333329)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '128G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(9132950.0, 9132950.0, 0.002142, 0.0, 0.0333327), (9067670.0, 9067670.0, 0.00215753, 0.0, 0.0166665), (9110610.0, 9110610.0, 0.00214738, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '133G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(11138600.0, 11138600.0, 0.00210667, 0.0, 0.0), (11086900.0, 11086900.0, 0.00211596, 0.0, 0.0), (11034700.0, 11034700.0, 0.00212673, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '133G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(9980690.0, 9980690.0, 0.00235595, 0.0, 0.0), (10055300.0, 10055300.0, 0.00233887, 0.0, 0.0166665), (10016900.0, 10016900.0, 0.00234774, 0.0, 0.0333326)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '133G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(10661500.0, 10661500.0, 0.00220307, 0.0, 0.0), (10694500.0, 10694500.0, 0.00219619, 0.0, 0.033333), (10674900.0, 10674900.0, 0.0022003, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '139G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(13132600.0, 13132600.0, 0.00208421, 0.0, 0.0), (13061100.0, 13061100.0, 0.00209564, 0.0, 0.0), (13131600.0, 13131600.0, 0.00208361, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '139G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(9574580.0, 9574580.0, 0.00287661, 0.0, 0.0166664), (9575250.0, 9575250.0, 0.00287639, 0.0, 0.0166664), (9678110.0, 9678110.0, 0.00284494, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '139G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(12592400.0, 12592400.0, 0.00217546, 0.0, 0.0333329), (12568800.0, 12568800.0, 0.00217985, 0.0, 0.0333325), (12316600.0, 12316600.0, 0.00222484, 0.0, 0.0166664)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '144G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(14762500.0, 14762500.0, 0.00211941, 0.0, 0.0), (14779900.0, 14779900.0, 0.00211705, 0.0, 0.0), (14757300.0, 14757300.0, 0.00212033, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '144G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(8929980.0, 8929980.0, 0.0035352, 0.0, 0.0), (8895050.0, 8895050.0, 0.00354928, 0.0, 0.0166664), (8940840.0, 8940840.0, 0.00353037, 0.0, 0.0166664)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 320000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '144G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(14090400.0, 14090400.0, 0.00222263, 0.0, 0.0166632), (14107400.0, 14107400.0, 0.00221994, 0.0, 0.0333329), (14118600.0, 14118600.0, 0.0022184, 0.0, 0.0333329)])]
diff --git a/silo/benchmarks/results/istc3-8-22-13_cameraready.py b/silo/benchmarks/results/istc3-8-22-13_cameraready.py
new file mode 100644 (file)
index 0000000..58156a3
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(28581.6, 28581.6, 0.0348721, 84.0771, 0.0), (28602.5, 28602.5, 0.0348454, 82.8083, 0.0), (28462.4, 28462.4, 0.0350136, 83.5707, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(28233.0, 28233.0, 0.0353046, 85.8589, 0.0), (28675.2, 28675.2, 0.0347586, 80.8825, 0.0), (28770.5, 28770.5, 0.0346354, 83.7797, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(30384.4, 30384.4, 0.0328257, 0.0, 0.0), (30370.2, 30370.2, 0.0328381, 0.0, 0.0), (30603.3, 30603.3, 0.0325872, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(25631.9, 25631.9, 0.0388824, 86.3268, 0.0), (25497.7, 25497.7, 0.0391135, 82.3829, 0.0), (25305.3, 25305.3, 0.0393871, 86.8797, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(25144.7, 25144.7, 0.0396357, 84.7745, 0.0), (25611.3, 25611.3, 0.0389202, 84.2683, 0.0), (25066.7, 25066.7, 0.0397612, 89.0185, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(26999.5, 26999.5, 0.0369491, 0.0, 0.0), (27038.9, 27038.9, 0.0368896, 0.0, 0.0), (27030.5, 27030.5, 0.0369025, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(102033.0, 102033.0, 0.039075, 116.086, 4.24616), (101811.0, 101811.0, 0.0391514, 108.753, 4.32904), (101613.0, 101613.0, 0.0392468, 116.108, 4.33032)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(102238.0, 102238.0, 0.0389833, 93.8575, 4.16197), (102146.0, 102146.0, 0.0390133, 95.4447, 4.26152), (102410.0, 102410.0, 0.0389261, 95.6984, 4.11312)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(109854.0, 109854.0, 0.036318, 0.0, 5.04994), (108880.0, 108880.0, 0.0366435, 0.0, 4.16664), (108802.0, 108802.0, 0.0366672, 0.0, 4.51663)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(91573.6, 91573.6, 0.043533, 94.3058, 2.68056), (91358.8, 91358.8, 0.0436126, 116.024, 2.51268), (91581.8, 91581.8, 0.04354, 96.238, 2.24792)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(91837.7, 91837.7, 0.0434038, 103.306, 2.83012), (92498.9, 92498.9, 0.0431005, 102.537, 2.59721), (91561.9, 91561.9, 0.0435366, 92.6303, 2.71367)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(96777.6, 96777.6, 0.0412297, 0.0, 2.79997), (96094.7, 96094.7, 0.0415237, 0.0, 2.56665), (96149.3, 96149.3, 0.0415024, 0.0, 2.93332)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(190403.0, 190403.0, 0.0418729, 138.159, 7.87489), (187809.0, 187809.0, 0.0424441, 100.639, 7.75646), (185207.0, 185207.0, 0.0425577, 177.304, 8.16468)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(190805.0, 190805.0, 0.0417944, 101.474, 8.07568), (191865.0, 191865.0, 0.0415614, 96.7059, 8.22492), (191391.0, 191391.0, 0.0416554, 99.4986, 7.30902)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(206900.0, 206900.0, 0.0385719, 0.0, 9.09978), (207548.0, 207548.0, 0.0384517, 0.0, 9.06647), (205381.0, 205381.0, 0.0388612, 0.0, 9.41648)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(173500.0, 173500.0, 0.0459583, 166.703, 5.24425), (171314.0, 171314.0, 0.0465562, 121.944, 5.19582), (173732.0, 173732.0, 0.0458952, 142.919, 5.36053)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(172978.0, 172978.0, 0.0461067, 93.2365, 5.16248), (174523.0, 174523.0, 0.0457, 105.733, 5.04557), (175544.0, 175544.0, 0.0454341, 107.263, 4.97892)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(184680.0, 184680.0, 0.0432207, 0.0, 4.96662), (181980.0, 181980.0, 0.0438596, 0.0, 5.09995), (184128.0, 184128.0, 0.0433499, 0.0, 5.39994)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(282551.0, 282551.0, 0.0423176, 124.085, 11.9193), (281383.0, 281383.0, 0.0424913, 173.96, 11.5686), (279719.0, 279719.0, 0.0427516, 161.055, 11.0876)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(285786.0, 285786.0, 0.0418578, 102.085, 12.5737), (281282.0, 281282.0, 0.0425319, 100.516, 11.6916), (285913.0, 285913.0, 0.0418388, 103.829, 12.1077)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(307865.0, 307865.0, 0.0388836, 0.0, 13.6999), (307560.0, 307560.0, 0.0389219, 0.0, 14.1498), (304962.0, 304962.0, 0.0392521, 0.0, 13.5497)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(254450.0, 254450.0, 0.0470049, 146.707, 7.40828), (257875.0, 257875.0, 0.0463946, 196.539, 7.4782), (257060.0, 257060.0, 0.0465216, 185.46, 7.44082)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(256678.0, 256678.0, 0.0465896, 96.0207, 7.80751), (258295.0, 258295.0, 0.0462982, 95.7259, 6.92513), (258533.0, 258533.0, 0.0462594, 97.0189, 7.62485)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(274121.0, 274121.0, 0.0436743, 0.0, 8.04985), (273844.0, 273844.0, 0.0437198, 0.0, 8.26659), (274565.0, 274565.0, 0.0436082, 0.0, 8.68317)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(371245.0, 371245.0, 0.0428847, 178.863, 14.8604), (365105.0, 365105.0, 0.0430223, 189.756, 14.5964), (372664.0, 372664.0, 0.0427771, 251.848, 14.9127)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(378012.0, 378012.0, 0.0421861, 106.449, 15.9495), (378407.0, 378407.0, 0.0421463, 106.281, 15.2035), (379336.0, 379336.0, 0.0420492, 97.747, 15.156)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(402865.0, 402865.0, 0.0396181, 0.0, 17.332), (402822.0, 402822.0, 0.0396222, 0.0, 16.8644), (405851.0, 405851.0, 0.0393262, 0.0, 18.8818)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(337543.0, 337543.0, 0.047233, 202.524, 9.92075), (338362.0, 338362.0, 0.0471185, 220.232, 9.55321), (340017.0, 340017.0, 0.046891, 249.777, 9.87118)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(339413.0, 339413.0, 0.0469776, 114.482, 9.15526), (340671.0, 340671.0, 0.0468063, 96.959, 9.53925), (344088.0, 344088.0, 0.0463691, 114.054, 9.57796)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(362596.0, 362596.0, 0.0440203, 0.0, 10.8832), (360304.0, 360304.0, 0.0443018, 0.0, 10.2998), (361802.0, 361802.0, 0.04412, 0.0, 11.2498)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(457057.0, 457057.0, 0.0436033, 223.184, 19.1931), (465981.0, 465981.0, 0.0427698, 176.921, 19.6938), (456816.0, 456816.0, 0.0436322, 239.539, 18.6467)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(475511.0, 475511.0, 0.0419239, 108.85, 18.9492), (473337.0, 473337.0, 0.0420988, 100.572, 19.3759), (473696.0, 473696.0, 0.0420733, 107.531, 19.8091)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(502112.0, 502112.0, 0.0397368, 0.0, 23.333), (504345.0, 504345.0, 0.0395582, 0.0, 22.0956), (504367.0, 504367.0, 0.0395524, 0.0, 22.5995)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(425761.0, 425761.0, 0.0468164, 193.99, 11.9535), (426636.0, 426636.0, 0.0467402, 231.505, 12.2418), (421637.0, 421637.0, 0.0472769, 252.581, 12.37)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(429522.0, 429522.0, 0.0464213, 114.885, 11.8401), (424845.0, 424845.0, 0.0469283, 97.2517, 11.9559), (430225.0, 430225.0, 0.0463448, 115.266, 12.7561)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(447396.0, 447396.0, 0.0446023, 0.0, 13.2831), (452285.0, 452285.0, 0.044116, 0.0, 12.7498), (453371.0, 453371.0, 0.0440071, 0.0, 12.9469)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(552020.0, 552020.0, 0.0429334, 490.78, 21.4949), (548774.0, 548774.0, 0.0435793, 300.284, 21.9052), (549327.0, 549327.0, 0.0435437, 349.779, 21.5926)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(558812.0, 558812.0, 0.0428157, 109.199, 23.4636), (557852.0, 557852.0, 0.0428741, 108.9, 23.0735), (560258.0, 560258.0, 0.0426928, 101.417, 23.9582)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(601613.0, 601613.0, 0.0397974, 0.0, 26.633), (598201.0, 598201.0, 0.0400262, 0.0, 27.2279), (594152.0, 594152.0, 0.0402956, 0.0, 25.8754)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(502036.0, 502036.0, 0.0476424, 418.618, 14.1489), (504214.0, 504214.0, 0.0474485, 248.564, 14.8526), (500780.0, 500780.0, 0.0477532, 326.528, 14.2482)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(502606.0, 502606.0, 0.0475962, 118.022, 14.3026), (509099.0, 509099.0, 0.0469954, 117.879, 14.8704), (508311.0, 508311.0, 0.0470499, 117.516, 14.2495)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(537301.0, 537301.0, 0.0445649, 0.0, 17.1663), (539009.0, 539009.0, 0.0444205, 0.0, 15.0978), (527390.0, 527390.0, 0.0454046, 0.0, 16.3329)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(623661.0, 623661.0, 0.044735, 130.327, 25.5491), (630421.0, 630421.0, 0.0442648, 207.427, 24.8565), (629670.0, 629670.0, 0.0443212, 140.857, 25.2746)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(640440.0, 640440.0, 0.0435831, 112.309, 26.8904), (638178.0, 638178.0, 0.0437352, 112.442, 26.2754), (636365.0, 636365.0, 0.0438555, 111.845, 25.971)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(690378.0, 690378.0, 0.0404617, 0.0, 30.132), (689311.0, 689311.0, 0.0405206, 0.0, 29.3142), (689306.0, 689306.0, 0.0405216, 0.0, 30.7158)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(573420.0, 573420.0, 0.0486641, 133.812, 16.1471), (570821.0, 570821.0, 0.0488872, 128.48, 15.8819), (567837.0, 567837.0, 0.0491639, 114.833, 16.7374)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(576329.0, 576329.0, 0.04843, 119.866, 16.4995), (577427.0, 577427.0, 0.0483374, 119.106, 15.7518), (578257.0, 578257.0, 0.048274, 120.102, 16.4361)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(616882.0, 616882.0, 0.0452864, 0.0, 18.5497), (617558.0, 617558.0, 0.045233, 0.0, 18.3996), (607894.0, 607894.0, 0.0459614, 0.0, 18.8329)])]+[({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(662954.0, 662954.0, 0.0471049, 2429.49, 27.4553), (659281.0, 659281.0, 0.0471141, 2522.32, 26.8881), (664766.0, 664766.0, 0.0472818, 2013.89, 27.4348)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(704843.0, 704843.0, 0.0452273, 927.15, 29.4923), (702699.0, 702699.0, 0.0453462, 718.997, 29.4776), (708851.0, 708851.0, 0.0449806, 715.551, 28.3624)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 45,43,4,4,4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(793687.0, 793687.0, 0.0402152, 0.0, 36.0826), (796774.0, 796774.0, 0.0400538, 0.0, 35.1118), (777733.0, 777733.0, 0.0410367, 0.0, 35.7484)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(625800.0, 625800.0, 0.0509602, 862.335, 17.8348), (625680.0, 625680.0, 0.0509713, 889.848, 16.9672), (624682.0, 624682.0, 0.0510143, 750.333, 17.5048)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-temp', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(638791.0, 638791.0, 0.0499017, 954.426, 18.66), (637160.0, 637160.0, 0.0500432, 450.39, 18.2134), (641167.0, 641167.0, 0.0497459, 668.748, 17.3197)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(712978.0, 712978.0, 0.0447699, 0.0, 21.3485), (709582.0, 709582.0, 0.044985, 0.0, 20.0829), (709746.0, 709746.0, 0.044968, 0.0, 21.2122)])]
diff --git a/silo/benchmarks/results/istc3-8-22-13_cameraready_2.py b/silo/benchmarks/results/istc3-8-22-13_cameraready_2.py
new file mode 100644 (file)
index 0000000..93ff239
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(761925.0, 761925.0, 0.036687, 0.0, 0.0), (755236.0, 755236.0, 0.0370134, 0.0, 0.0), (761988.0, 761988.0, 0.0366857, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(741514.0, 741514.0, 0.0376888, 0.0, 112.514), (739086.0, 739086.0, 0.0378125, 0.0, 113.831), (740218.0, 740218.0, 0.0377549, 0.0, 114.347)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(743874.0, 743874.0, 0.0375595, 0.0, 226.745), (742293.0, 742293.0, 0.0376404, 0.0, 224.595), (694439.0, 694439.0, 0.0402369, 0.0, 212.695)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(739433.0, 739433.0, 0.0377757, 0.0, 339.36), (738721.0, 738721.0, 0.0378116, 0.0, 336.921), (740819.0, 740819.0, 0.0377048, 0.0, 334.977)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(724033.0, 724033.0, 0.038567, 0.0, 439.057), (737439.0, 737439.0, 0.0378762, 0.0, 449.04), (711693.0, 711693.0, 0.0392472, 0.0, 431.058)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(720801.0, 720801.0, 0.0387317, 0.0, 544.489), (734749.0, 734749.0, 0.037999, 0.0, 554.856), (731802.0, 731802.0, 0.0381505, 0.0, 555.039)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(717564.0, 717564.0, 0.0389012, 0.0, 641.42), (717539.0, 717539.0, 0.0389021, 0.0, 639.789), (726509.0, 726509.0, 0.0384251, 0.0, 652.853)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(716533.0, 716533.0, 0.0389462, 0.0, 742.653), (723216.0, 723216.0, 0.0385864, 0.0, 761.198), (719808.0, 719808.0, 0.0387761, 0.0, 754.219)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(723667.0, 723667.0, 0.0385537, 0.0, 852.167), (713627.0, 713627.0, 0.0391009, 0.0, 848.684), (713430.0, 713430.0, 0.0391072, 0.0, 844.501)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(698474.0, 698474.0, 0.0399392, 0.0, 926.317), (715089.0, 715089.0, 0.0390088, 0.0, 956.292), (698082.0, 698082.0, 0.0399615, 0.0, 931.414)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(713678.0, 713678.0, 0.0390771, 0.0, 1048.95), (693198.0, 693198.0, 0.0402394, 0.0, 1007.36), (708960.0, 708960.0, 0.0393372, 0.0, 1044.95)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(764516.0, 764516.0, 0.036567, 0.0, 0.0), (753562.0, 753562.0, 0.0370983, 0.0, 0.0), (768736.0, 768736.0, 0.0363656, 0.0, 0.0)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(761984.0, 761984.0, 0.0366779, 0.0, 116.365), (758150.0, 758150.0, 0.0368636, 0.0, 115.514), (748610.0, 748610.0, 0.037333, 0.0, 116.514)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(752947.0, 752947.0, 0.0371113, 0.0, 233.529), (753083.0, 753083.0, 0.0371008, 0.0, 232.029), (753469.0, 753469.0, 0.0370838, 0.0, 230.713)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(732446.0, 732446.0, 0.0381398, 0.0, 340.393), (732461.0, 732461.0, 0.0381376, 0.0, 339.644), (747909.0, 747909.0, 0.0373479, 0.0, 343.194)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(751076.0, 751076.0, 0.0371826, 0.0, 460.576), (747916.0, 747916.0, 0.0373426, 0.0, 457.438), (740021.0, 740021.0, 0.0377378, 0.0, 454.891)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(746069.0, 746069.0, 0.0374253, 0.0, 567.739), (742668.0, 742668.0, 0.0375976, 0.0, 566.224), (737383.0, 737383.0, 0.0378633, 0.0, 559.706)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(736851.0, 736851.0, 0.037882, 0.0, 675.005), (742248.0, 742248.0, 0.0376062, 0.0, 677.336), (738785.0, 738785.0, 0.0377861, 0.0, 674.681)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(725477.0, 725477.0, 0.038467, 0.0, 770.969), (733751.0, 733751.0, 0.038038, 0.0, 773.802), (733336.0, 733336.0, 0.0380546, 0.0, 782.502)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(735556.0, 735556.0, 0.0379309, 0.0, 889.63), (723751.0, 723751.0, 0.0385487, 0.0, 870.399), (724768.0, 724768.0, 0.0384961, 0.0, 865.635)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(714283.0, 714283.0, 0.0390534, 0.0, 959.65), (728052.0, 728052.0, 0.0383148, 0.0, 981.798), (723395.0, 723395.0, 0.0385579, 0.0, 971.409)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(721641.0, 721641.0, 0.038646, 0.0, 1079.56), (708074.0, 708074.0, 0.0393894, 0.0, 1039.58), (731489.0, 731489.0, 0.0381238, 0.0, 1097.68)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(916252.0, 916252.0, 0.0305083, 0.0, 0.0), (917521.0, 917521.0, 0.0304585, 0.0, 0.0), (915088.0, 915088.0, 0.0305415, 0.0, 0.0)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(901833.0, 901833.0, 0.0309844, 0.0, 137.498), (896487.0, 896487.0, 0.0311668, 0.0, 137.647), (896755.0, 896755.0, 0.0311592, 0.0, 137.531)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(891766.0, 891766.0, 0.0313249, 0.0, 271.813), (898610.0, 898610.0, 0.0310878, 0.0, 272.43), (897235.0, 897235.0, 0.0311386, 0.0, 274.913)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(896572.0, 896572.0, 0.0311474, 0.0, 413.711), (897517.0, 897517.0, 0.0311156, 0.0, 411.444), (893463.0, 893463.0, 0.031254, 0.0, 404.577)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(894255.0, 894255.0, 0.0312222, 0.0, 535.059), (890013.0, 890013.0, 0.0313726, 0.0, 539.76), (886847.0, 886847.0, 0.0314858, 0.0, 537.927)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(869808.0, 869808.0, 0.0320924, 0.0, 648.923), (889337.0, 889337.0, 0.0313859, 0.0, 671.986), (885142.0, 885142.0, 0.0315384, 0.0, 664.491)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(880989.0, 880989.0, 0.0316803, 0.0, 793.356), (881858.0, 881858.0, 0.0316454, 0.0, 790.987), (879940.0, 879940.0, 0.0317201, 0.0, 792.57)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(880672.0, 880672.0, 0.0316803, 0.0, 915.722), (883545.0, 883545.0, 0.0315806, 0.0, 920.838), (860971.0, 860971.0, 0.0324068, 0.0, 893.522)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(851959.0, 851959.0, 0.0327449, 0.0, 1003.77), (868622.0, 868622.0, 0.0321134, 0.0, 1034.45), (872809.0, 872809.0, 0.0319597, 0.0, 1031.66)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(876583.0, 876583.0, 0.0318182, 0.0, 1163.23), (855208.0, 855208.0, 0.0326117, 0.0, 1131.17), (868075.0, 868075.0, 0.0321304, 0.0, 1152.82)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(867701.0, 867701.0, 0.0321374, 0.0, 1272.03), (855953.0, 855953.0, 0.0325811, 0.0, 1255.06), (860486.0, 860486.0, 0.0324038, 0.0, 1252.75)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(1232090.0, 1232090.0, 0.0226764, 0.0, 0.0), (1220140.0, 1220140.0, 0.0229003, 0.0, 0.0), (1242170.0, 1242170.0, 0.0224947, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(1021610.0, 1021610.0, 0.0273595, 0.0, 0.0), (1033550.0, 1033550.0, 0.0270438, 0.0, 0.0), (1020520.0, 1020520.0, 0.0273879, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(843155.0, 843155.0, 0.0331615, 0.0, 0.0), (834208.0, 834208.0, 0.0335171, 0.0, 0.0), (851254.0, 851254.0, 0.0328454, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(692836.0, 692836.0, 0.0403644, 0.0, 0.0), (703075.0, 703075.0, 0.0397767, 0.0, 0.0), (693779.0, 693779.0, 0.0403112, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(575992.0, 575992.0, 0.0485627, 0.0, 0.0), (580830.0, 580830.0, 0.0481582, 0.0, 0.0), (578876.0, 578876.0, 0.0483207, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(487289.0, 487289.0, 0.0574108, 0.0, 0.0), (486983.0, 486983.0, 0.0574478, 0.0, 0.0), (481985.0, 481985.0, 0.0580441, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(412714.0, 412714.0, 0.0677913, 0.0, 0.0), (415006.0, 415006.0, 0.0674203, 0.0, 0.0), (410488.0, 410488.0, 0.0681635, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(356538.0, 356538.0, 0.0784847, 0.0, 0.0), (354649.0, 354649.0, 0.0789013, 0.0, 0.0), (354405.0, 354405.0, 0.0789565, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(315910.0, 315910.0, 0.0885812, 0.0, 0.0), (314779.0, 314779.0, 0.0889002, 0.0, 0.0), (312993.0, 312993.0, 0.0894047, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(280556.0, 280556.0, 0.099751, 0.0, 0.0), (281837.0, 281837.0, 0.0992975, 0.0, 0.0), (281193.0, 281193.0, 0.0995241, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(250461.0, 250461.0, 0.111738, 0.0, 0.0), (253391.0, 253391.0, 0.11045, 0.0, 0.0), (253295.0, 253295.0, 0.110493, 0.0, 0.0)])]
diff --git a/silo/benchmarks/results/istc3-8-23-13_cameraready.py b/silo/benchmarks/results/istc3-8-23-13_cameraready.py
new file mode 100644 (file)
index 0000000..ccb1038
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-factor-gc-nowriteinplace/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(378558.0, 378558.0, 0.0738352, 0.0, 14.9164), (376220.0, 376220.0, 0.0742913, 0.0, 14.3164), (378809.0, 378809.0, 0.0737782, 0.0, 14.1664)]), ({'binary': '../out-factor-gc-nowriteinplace/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(453376.0, 453376.0, 0.0616387, 0.0, 17.283), (462089.0, 462089.0, 0.0604906, 0.0, 17.0163), (461377.0, 461377.0, 0.0605727, 0.0, 18.1997)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(607920.0, 607920.0, 0.0459504, 0.0, 17.6995), (613208.0, 613208.0, 0.0455548, 0.0, 17.7486), (612916.0, 612916.0, 0.0455771, 0.0, 17.6164)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(669453.0, 669453.0, 0.0417192, 0.0, 20.6492), (663652.0, 663652.0, 0.0420858, 0.0, 20.8492), (670641.0, 670641.0, 0.041646, 0.0, 20.016)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': True, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'log_nofsync': False, 'disable_snapshots': True, 'log_compress': False}, [(676449.0, 676449.0, 0.0412916, 0.0, 20.7495), (673556.0, 673556.0, 0.0414636, 0.0, 22.266), (681930.0, 681930.0, 0.0409544, 0.0, 21.0328)])]
diff --git a/silo/benchmarks/results/istc3-8-24-13_cameraready.py b/silo/benchmarks/results/istc3-8-24-13_cameraready.py
new file mode 100644 (file)
index 0000000..046522a
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-factor-fake-compression/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(532579.0, 532579.0, 0.0523977, 116.868, 15.2999), (530511.0, 530511.0, 0.0526062, 120.144, 15.4506), (523314.0, 523314.0, 0.0533354, 115.421, 14.8863)]), ({'binary': '../out-factor-fake-compression/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(608186.0, 608186.0, 0.0459001, 117.062, 18.0027), (612484.0, 612484.0, 0.0455733, 116.841, 18.1505), (607283.0, 607283.0, 0.0459702, 104.865, 18.0365)]), ({'binary': '../out-perf/new-benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(467031.0, 467031.0, 0.0594358, 115.534, 3564.88), (464451.0, 464451.0, 0.0597498, 125.002, 3475.16), (472428.0, 472428.0, 0.0586882, 115.636, 4400.58)]), ({'binary': '../out-perf/new-benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': False}, [(521674.0, 521674.0, 0.0531624, 122.404, 4328.4), (524501.0, 524501.0, 0.0527698, 123.597, 5020.51), (526127.0, 526127.0, 0.0526186, 119.165, 4947.64)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': True}, [(479742.0, 479742.0, 0.0581989, 121.353, 13.9398), (476630.0, 476630.0, 0.0585747, 162.718, 13.4896), (482217.0, 482217.0, 0.0578746, 165.733, 13.0846)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '--workload-mix 39,37,4,10,10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'log_nofsync': False, 'disable_snapshots': False, 'log_compress': True}, [(544413.0, 544413.0, 0.0512864, 171.96, 14.6895), (544332.0, 544332.0, 0.0512839, 184.383, 14.8198), (545894.0, 545894.0, 0.0511259, 120.204, 15.6496)])]
diff --git a/silo/benchmarks/results/istc3-9-6-13.py b/silo/benchmarks/results/istc3-9-6-13.py
new file mode 100644 (file)
index 0000000..54fdd07
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(452042.0, 452042.0, 0.00216524, 0.0, 0.0), (464864.0, 464864.0, 0.00210337, 0.0, 0.0), (466241.0, 466241.0, 0.00209754, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(437983.0, 437983.0, 0.0022322, 0.0, 0.0), (442841.0, 442841.0, 0.0022081, 0.0, 0.0), (446022.0, 446022.0, 0.00219415, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(451615.0, 451615.0, 0.00216578, 0.0, 0.0), (461024.0, 461024.0, 0.00212142, 0.0, 0.0), (457708.0, 457708.0, 0.00213468, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(1921590.0, 1921590.0, 0.00203194, 0.0, 0.0), (1926810.0, 1926810.0, 0.00202617, 0.0, 0.0), (1913600.0, 1913600.0, 0.00204084, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(1767270.0, 1767270.0, 0.00219573, 0.0, 0.0), (1792550.0, 1792550.0, 0.00215962, 0.0, 0.0), (1767530.0, 1767530.0, 0.00219522, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(1833020.0, 1833020.0, 0.00212994, 0.0, 0.0), (1838520.0, 1838520.0, 0.00212361, 0.0, 0.0), (1838180.0, 1838180.0, 0.00212396, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(3712160.0, 3712160.0, 0.0021053, 0.0, 0.0), (3644190.0, 3644190.0, 0.00212426, 0.0, 0.0), (3715240.0, 3715240.0, 0.00210303, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(3471230.0, 3471230.0, 0.00223265, 0.0, 0.0), (3517710.0, 3517710.0, 0.00222198, 0.0, 0.0), (3480990.0, 3480990.0, 0.00222303, 0.0, 0.0166666)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(3538120.0, 3538120.0, 0.00221022, 0.0, 0.0), (3486620.0, 3486620.0, 0.00224206, 0.0, 0.0), (3546780.0, 3546780.0, 0.00220471, 0.0, 0.0166666)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(5054530.0, 5054530.0, 0.00232453, 0.0, 0.0), (5066520.0, 5066520.0, 0.00231886, 0.0, 0.0), (5040760.0, 5040760.0, 0.00233107, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(4823480.0, 4823480.0, 0.00243508, 0.0, 0.0), (4858860.0, 4858860.0, 0.00241689, 0.0, 0.0166666), (4779740.0, 4779740.0, 0.00244918, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(4879320.0, 4879320.0, 0.00240545, 0.0, 0.0), (4888500.0, 4888500.0, 0.00240203, 0.0, 0.0166665), (4901180.0, 4901180.0, 0.00239799, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(6522230.0, 6522230.0, 0.00238239, 0.0, 0.0), (6619740.0, 6619740.0, 0.00236734, 0.0, 0.0), (6604000.0, 6604000.0, 0.00237299, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(6325430.0, 6325430.0, 0.0024773, 0.0, 0.0333329), (6338340.0, 6338340.0, 0.00247133, 0.0, 0.0), (6250710.0, 6250710.0, 0.00249825, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(6398160.0, 6398160.0, 0.00244992, 0.0, 0.0), (6248970.0, 6248970.0, 0.00250899, 0.0, 0.033333), (6318810.0, 6318810.0, 0.0024569, 0.0, 0.0166665)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(8201050.0, 8201050.0, 0.00238911, 0.0, 0.0), (8168090.0, 8168090.0, 0.0023989, 0.0, 0.0), (8188650.0, 8188650.0, 0.0023926, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(7624520.0, 7624520.0, 0.00257256, 0.0, 0.0499994), (7777670.0, 7777670.0, 0.00252113, 0.0, 0.0), (7772230.0, 7772230.0, 0.00252295, 0.0, 0.0166664)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(7875170.0, 7875170.0, 0.00248672, 0.0, 0.0), (7916410.0, 7916410.0, 0.00247345, 0.0, 0.0), (7913710.0, 7913710.0, 0.00247439, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(9675690.0, 9675690.0, 0.00243063, 0.0, 0.0), (9686230.0, 9686230.0, 0.0024281, 0.0, 0.0), (9676750.0, 9676750.0, 0.00243037, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(8974170.0, 8974170.0, 0.00261314, 0.0, 0.0333329), (9048450.0, 9048450.0, 0.0025994, 0.0, 0.0166664), (8872590.0, 8872590.0, 0.00263662, 0.0, 0.0166665)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(9271600.0, 9271600.0, 0.00251357, 0.0, 0.033333), (9381340.0, 9381340.0, 0.00250479, 0.0, 0.033333), (9404460.0, 9404460.0, 0.00250152, 0.0, 0.033333)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(11296300.0, 11296300.0, 0.00242902, 0.0, 0.0), (11324100.0, 11324100.0, 0.00242303, 0.0, 0.0), (11338600.0, 11338600.0, 0.00241994, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(9701690.0, 9701690.0, 0.0028357, 0.0, 0.0666656), (9761020.0, 9761020.0, 0.00281746, 0.0, 0.0166662), (9729960.0, 9729960.0, 0.00282581, 0.0, 0.0333325)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(10683700.0, 10683700.0, 0.00257016, 0.0, 0.0833313), (10929000.0, 10929000.0, 0.00250912, 0.0, 0.0), (10981500.0, 10981500.0, 0.00249772, 0.0, 0.033332)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(12647100.0, 12647100.0, 0.00248038, 0.0, 0.0), (12646200.0, 12646200.0, 0.0024805, 0.0, 0.0), (12694000.0, 12694000.0, 0.00247065, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(9311740.0, 9311740.0, 0.00338524, 0.0, 0.0166643), (9362850.0, 9362850.0, 0.00334534, 0.0, 0.0499993), (9588760.0, 9588760.0, 0.00328362, 0.0, 0.0333264)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(12222600.0, 12222600.0, 0.0025649, 0.0, 0.0666657), (12214100.0, 12214100.0, 0.00256708, 0.0, 0.0333328), (12216600.0, 12216600.0, 0.00256603, 0.0, 0.0499896)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(27538.3, 27538.3, 0.0361888, 83.0556, 0.0), (27492.9, 27492.9, 0.0362428, 86.853, 0.0), (26757.2, 26757.2, 0.0372377, 83.0574, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(27578.6, 27578.6, 0.036121, 80.2099, 0.0), (27030.2, 27030.2, 0.0368578, 81.0188, 0.0), (27458.4, 27458.4, 0.0362976, 84.3511, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(29180.6, 29180.6, 0.0341653, 0.0, 0.0), (29135.3, 29135.3, 0.0342217, 0.0, 0.0), (28736.5, 28736.5, 0.0346753, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(96007.7, 96007.7, 0.0414997, 94.7939, 3.63077), (95666.0, 95666.0, 0.0416369, 94.9036, 3.63068), (95873.0, 95873.0, 0.0415698, 110.137, 4.09709)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(95466.6, 95466.6, 0.04174, 97.0183, 3.71276), (96668.8, 96668.8, 0.0412194, 97.7066, 4.46162), (95564.9, 95564.9, 0.0416969, 93.0297, 3.91266)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(102773.0, 102773.0, 0.0388068, 0.0, 4.48328), (103817.0, 103817.0, 0.0384017, 0.0, 4.51662), (102911.0, 102911.0, 0.0387586, 0.0, 4.44997)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(180394.0, 180394.0, 0.0441757, 105.423, 7.00823), (179518.0, 179518.0, 0.0444062, 126.829, 7.22653), (178611.0, 178611.0, 0.0446177, 117.303, 7.3108)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(181509.0, 181509.0, 0.0439308, 94.9045, 6.89569), (181726.0, 181726.0, 0.0438724, 99.8244, 7.87748), (180855.0, 180855.0, 0.044076, 100.578, 8.32576)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(195073.0, 195073.0, 0.0408981, 0.0, 8.14993), (194432.0, 194432.0, 0.0410341, 0.0, 8.38325), (195370.0, 195370.0, 0.040833, 0.0, 8.61655)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(268938.0, 268938.0, 0.0444439, 149.54, 11.269), (265761.0, 265761.0, 0.0449617, 159.273, 10.3899), (266935.0, 266935.0, 0.0441493, 170.926, 10.7511)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(276264.0, 276264.0, 0.043269, 105.131, 11.3872), (276787.0, 276787.0, 0.0432036, 95.3977, 11.3736), (277126.0, 277126.0, 0.0431518, 105.348, 10.4912)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(293333.0, 293333.0, 0.0407696, 0.0, 12.4497), (292780.0, 292780.0, 0.0408571, 0.0, 13.2498), (291534.0, 291534.0, 0.0410375, 0.0, 13.8497)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(352427.0, 352427.0, 0.0452315, 222.677, 13.9848), (348421.0, 348421.0, 0.0457489, 246.85, 13.9664), (352008.0, 352008.0, 0.0452919, 219.16, 14.3365)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(359112.0, 359112.0, 0.0444021, 107.038, 14.6608), (357159.0, 357159.0, 0.0446192, 98.1228, 14.7699), (358054.0, 358054.0, 0.0445315, 106.795, 15.2039)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(381875.0, 381875.0, 0.0417836, 0.0, 16.7313), (377503.0, 377503.0, 0.0422643, 0.0, 17.6159), (380584.0, 380584.0, 0.0419252, 0.0, 16.1156)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(433175.0, 433175.0, 0.0460038, 212.165, 16.9328), (437184.0, 437184.0, 0.0450801, 260.417, 17.9312), (442212.0, 442212.0, 0.0450386, 197.142, 18.0396)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(452979.0, 452979.0, 0.043993, 107.888, 18.0369), (448373.0, 448373.0, 0.0444015, 111.046, 17.58), (453081.0, 453081.0, 0.0439962, 99.2393, 17.9533)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(484640.0, 484640.0, 0.0411549, 0.0, 20.2161), (488753.0, 488753.0, 0.040809, 0.0, 20.3997), (479245.0, 479245.0, 0.0415849, 0.0, 19.6497)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(525248.0, 525248.0, 0.0455121, 242.057, 21.0699), (523367.0, 523367.0, 0.0456968, 193.988, 20.3312), (524602.0, 524602.0, 0.0455, 321.327, 20.7913)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(532022.0, 532022.0, 0.0449575, 105.089, 22.0334), (534329.0, 534329.0, 0.0447697, 111.91, 21.4019), (536476.0, 536476.0, 0.044561, 111.151, 21.4223)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(570732.0, 570732.0, 0.0419374, 0.0, 23.9329), (571047.0, 571047.0, 0.0419124, 0.0, 23.8163), (570918.0, 570918.0, 0.0419215, 0.0, 24.7761)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(598686.0, 598686.0, 0.0465895, 174.449, 23.4482), (600258.0, 600258.0, 0.046462, 162.084, 24.6326), (598717.0, 598717.0, 0.046568, 311.776, 24.3769)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(603890.0, 603890.0, 0.0461928, 112.448, 24.6044), (596718.0, 596718.0, 0.0467293, 111.342, 24.1922), (608729.0, 608729.0, 0.0458189, 112.189, 26.3649)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(651817.0, 651817.0, 0.0428116, 0.0, 26.7657), (656421.0, 656421.0, 0.0425398, 0.0, 28.0488), (660473.0, 660473.0, 0.0422742, 0.0, 28.3763)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(644787.0, 644787.0, 0.049389, 590.73, 26.9662), (656987.0, 656987.0, 0.0483292, 835.787, 25.5428), (657270.0, 657270.0, 0.0482841, 1012.16, 26.7783)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(678882.0, 678882.0, 0.0469296, 842.415, 28.2716), (669076.0, 669076.0, 0.0476558, 453.983, 27.5187), (665170.0, 665170.0, 0.0478988, 386.233, 27.6094)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(765890.0, 765890.0, 0.0416563, 0.0, 34.766), (755041.0, 755041.0, 0.0422457, 0.0, 33.1598), (749524.0, 749524.0, 0.042559, 0.0, 33.5291)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(760737.0, 760737.0, 0.0367443, 0.0, 0.0), (756585.0, 756585.0, 0.0369378, 0.0, 0.0), (748428.0, 748428.0, 0.0373201, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(736352.0, 736352.0, 0.0379472, 0.0, 108.314), (736097.0, 736097.0, 0.0379347, 0.0, 110.915), (741020.0, 741020.0, 0.0377052, 0.0, 110.781)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(723448.0, 723448.0, 0.0386128, 0.0, 214.33), (718911.0, 718911.0, 0.0388503, 0.0, 214.812), (731800.0, 731800.0, 0.0381717, 0.0, 219.429)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(734918.0, 734918.0, 0.0380018, 0.0, 328.96), (730728.0, 730728.0, 0.0382122, 0.0, 320.594), (718775.0, 718775.0, 0.0388285, 0.0, 321.241)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(707975.0, 707975.0, 0.0394355, 0.0, 416.492), (727129.0, 727129.0, 0.0384009, 0.0, 429.359), (726943.0, 726943.0, 0.0384061, 0.0, 434.107)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(692040.0, 692040.0, 0.040308, 0.0, 504.957), (721994.0, 721994.0, 0.038661, 0.0, 530.524), (708013.0, 708013.0, 0.0394314, 0.0, 516.141)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(718826.0, 718826.0, 0.0388202, 0.0, 631.537), (717853.0, 717853.0, 0.038875, 0.0, 631.061), (714846.0, 714846.0, 0.0390424, 0.0, 625.232)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(706458.0, 706458.0, 0.0394982, 0.0, 717.488), (695008.0, 695008.0, 0.0401466, 0.0, 706.137), (702387.0, 702387.0, 0.0397258, 0.0, 722.003)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(701751.0, 701751.0, 0.0397245, 0.0, 803.435), (707510.0, 707510.0, 0.0394277, 0.0, 827.283), (707377.0, 707377.0, 0.0394405, 0.0, 821.086)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(704369.0, 704369.0, 0.0395943, 0.0, 911.508), (694207.0, 694207.0, 0.040176, 0.0, 893.0), (704339.0, 704339.0, 0.0395913, 0.0, 917.744)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(704799.0, 704799.0, 0.0395601, 0.0, 1016.02), (695775.0, 695775.0, 0.0400784, 0.0, 998.515), (703833.0, 703833.0, 0.0396155, 0.0, 1012.73)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(728023.0, 728023.0, 0.0383993, 0.0, 0.0), (765268.0, 765268.0, 0.0365334, 0.0, 0.0), (764189.0, 764189.0, 0.0365702, 0.0, 0.0)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(755996.0, 755996.0, 0.0369549, 0.0, 114.197), (750716.0, 750716.0, 0.0372323, 0.0, 114.465), (750218.0, 750218.0, 0.0372571, 0.0, 114.664)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(752511.0, 752511.0, 0.0371191, 0.0, 229.246), (748679.0, 748679.0, 0.0373163, 0.0, 227.178), (756912.0, 756912.0, 0.0369055, 0.0, 226.962)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(742884.0, 742884.0, 0.0375954, 0.0, 332.826), (738827.0, 738827.0, 0.0378136, 0.0, 331.377), (744265.0, 744265.0, 0.0375378, 0.0, 332.578)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(724986.0, 724986.0, 0.0385223, 0.0, 435.425), (736227.0, 736227.0, 0.0379248, 0.0, 435.454), (741237.0, 741237.0, 0.0376792, 0.0, 451.392)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(736801.0, 736801.0, 0.0378862, 0.0, 547.14), (737716.0, 737716.0, 0.037841, 0.0, 549.898), (737309.0, 737309.0, 0.0378723, 0.0, 554.407)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(732697.0, 732697.0, 0.0380914, 0.0, 652.839), (728138.0, 728138.0, 0.0383327, 0.0, 644.57), (738829.0, 738829.0, 0.0377762, 0.0, 650.27)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(730904.0, 730904.0, 0.0381887, 0.0, 760.554), (715812.0, 715812.0, 0.0389897, 0.0, 737.384), (731317.0, 731317.0, 0.0381524, 0.0, 748.521)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(729032.0, 729032.0, 0.038264, 0.0, 865.983), (724778.0, 724778.0, 0.0384879, 0.0, 846.502), (731534.0, 731534.0, 0.0381356, 0.0, 859.499)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(709319.0, 709319.0, 0.0393346, 0.0, 934.615), (717715.0, 717715.0, 0.0388643, 0.0, 946.692), (716229.0, 716229.0, 0.0389539, 0.0, 940.517)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(719137.0, 719137.0, 0.0387861, 0.0, 1054.25), (724973.0, 724973.0, 0.0384603, 0.0, 1065.1), (723514.0, 723514.0, 0.038538, 0.0, 1048.11)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(854066.0, 854066.0, 0.0327204, 0.0, 0.0), (852372.0, 852372.0, 0.0327866, 0.0, 0.0), (842170.0, 842170.0, 0.033193, 0.0, 0.0)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(844104.0, 844104.0, 0.0331083, 0.0, 129.064), (840490.0, 840490.0, 0.0332385, 0.0, 124.048), (853847.0, 853847.0, 0.0327148, 0.0, 127.131)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(844633.0, 844633.0, 0.0330805, 0.0, 250.296), (840322.0, 840322.0, 0.0332488, 0.0, 252.962), (832127.0, 832127.0, 0.0335763, 0.0, 249.342)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(831417.0, 831417.0, 0.0335936, 0.0, 372.777), (841467.0, 841467.0, 0.0331792, 0.0, 372.343), (835749.0, 835749.0, 0.0334182, 0.0, 376.211)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(835356.0, 835356.0, 0.0334186, 0.0, 494.972), (821575.0, 821575.0, 0.0339817, 0.0, 484.092), (812035.0, 812035.0, 0.0343911, 0.0, 483.921)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(823976.0, 823976.0, 0.0338862, 0.0, 607.39), (829215.0, 829215.0, 0.0336586, 0.0, 613.24), (825950.0, 825950.0, 0.0337922, 0.0, 610.639)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(825304.0, 825304.0, 0.033811, 0.0, 728.849), (820991.0, 820991.0, 0.0340006, 0.0, 727.237), (815901.0, 815901.0, 0.0342117, 0.0, 720.008)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(815238.0, 815238.0, 0.0342296, 0.0, 831.135), (813051.0, 813051.0, 0.0343265, 0.0, 835.937), (818609.0, 818609.0, 0.0340772, 0.0, 836.069)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(811705.0, 811705.0, 0.0343658, 0.0, 944.4), (792589.0, 792589.0, 0.0352003, 0.0, 923.665), (795024.0, 795024.0, 0.0350929, 0.0, 929.318)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(792337.0, 792337.0, 0.0352103, 0.0, 1030.16), (792573.0, 792573.0, 0.0351861, 0.0, 1019.87), (809167.0, 809167.0, 0.0344651, 0.0, 1051.46)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(804000.0, 804000.0, 0.0346887, 0.0, 1168.68), (804058.0, 804058.0, 0.0346762, 0.0, 1164.0), (787078.0, 787078.0, 0.0354363, 0.0, 1139.12)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(1181230.0, 1181230.0, 0.0236538, 0.0, 0.0), (1166800.0, 1166800.0, 0.0239464, 0.0, 0.0), (1179850.0, 1179850.0, 0.0236783, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(971295.0, 971295.0, 0.0287749, 0.0, 0.0), (968253.0, 968253.0, 0.0288669, 0.0, 0.0), (973275.0, 973275.0, 0.0287167, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(810657.0, 810657.0, 0.0344889, 0.0, 0.0), (800640.0, 800640.0, 0.0349187, 0.0, 0.0), (799896.0, 799896.0, 0.0349515, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(662791.0, 662791.0, 0.0421835, 0.0, 0.0), (654332.0, 654332.0, 0.0427393, 0.0, 0.0), (663105.0, 663105.0, 0.0421728, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(552833.0, 552833.0, 0.0505958, 0.0, 0.0), (555669.0, 555669.0, 0.050337, 0.0, 0.0), (554818.0, 554818.0, 0.050412, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(463739.0, 463739.0, 0.0603213, 0.0, 0.0), (465112.0, 465112.0, 0.0601426, 0.0, 0.0), (466305.0, 466305.0, 0.0599932, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(385157.0, 385157.0, 0.0726439, 0.0, 0.0), (394612.0, 394612.0, 0.0708986, 0.0, 0.0), (393333.0, 393333.0, 0.0711282, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(340802.0, 340802.0, 0.0821019, 0.0, 0.0), (335568.0, 335568.0, 0.0833865, 0.0, 0.0), (343311.0, 343311.0, 0.0814972, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(301388.0, 301388.0, 0.0928434, 0.0, 0.0), (299738.0, 299738.0, 0.0933579, 0.0, 0.0), (301597.0, 301597.0, 0.0927779, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(267745.0, 267745.0, 0.104522, 0.0, 0.0), (266828.0, 266828.0, 0.10487, 0.0, 0.0), (266606.0, 266606.0, 0.104964, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(240171.0, 240171.0, 0.116524, 0.0, 0.0), (240236.0, 240236.0, 0.116491, 0.0, 0.0), (238061.0, 238061.0, 0.117557, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(38292.6, 38292.6, 0.0260605, 0.0, 0.0), (37907.0, 37907.0, 0.0263185, 0.0, 0.0), (36878.5, 36878.5, 0.0270556, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(27449.3, 27449.3, 0.036351, 0.0, 0.0), (27753.3, 27753.3, 0.0359516, 0.0, 0.0), (27094.8, 27094.8, 0.0368207, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(27641.0, 27641.0, 0.0361019, 0.0, 0.0), (27953.9, 27953.9, 0.0356978, 0.0, 0.0), (27457.8, 27457.8, 0.0363236, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(57652.2, 57652.2, 0.0345676, 0.0, 2.89998), (56670.7, 56670.7, 0.0351528, 0.0, 2.84998), (54917.8, 54917.8, 0.0362913, 0.0, 2.73331)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(58061.6, 58061.6, 0.0343619, 0.0, 2.96665), (57482.4, 57482.4, 0.0347208, 0.0, 2.96664), (58990.0, 58990.0, 0.0338333, 0.0, 2.88332)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(115410.0, 115410.0, 0.0344323, 0.0, 17.1166), (113899.0, 113899.0, 0.0349052, 0.0, 16.9832), (115116.0, 115116.0, 0.0345421, 0.0, 16.8999)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(119515.0, 119515.0, 0.0333832, 0.0, 19.0499), (107348.0, 107348.0, 0.037148, 0.0, 16.6499), (118260.0, 118260.0, 0.0337463, 0.0, 17.9333)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(117606.0, 117606.0, 0.0362444, 0.0, 1821.36), (121477.0, 121477.0, 0.0350092, 0.0, 1839.61), (121716.0, 121716.0, 0.0349849, 0.0, 1855.5)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(166855.0, 166855.0, 0.0355978, 0.0, 816.394), (168836.0, 168836.0, 0.0351889, 0.0, 834.01), (168706.0, 168706.0, 0.0352278, 0.0, 829.311)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(129422.0, 129422.0, 0.0350511, 0.0, 3738.71), (131922.0, 131922.0, 0.0343259, 0.0, 3738.77), (128203.0, 128203.0, 0.0353515, 0.0, 3723.92)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(219029.0, 219029.0, 0.036051, 0.0, 1653.34), (223050.0, 223050.0, 0.0353896, 0.0, 1683.79), (217087.0, 217087.0, 0.0363719, 0.0, 1631.48)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(134115.0, 134115.0, 0.035721, 0.0, 5419.21), (132831.0, 132831.0, 0.0360902, 0.0, 5383.39), (133570.0, 133570.0, 0.0358967, 0.0, 5408.46)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(275315.0, 275315.0, 0.0356079, 0.0, 3265.04), (267270.0, 267270.0, 0.0366779, 0.0, 3196.41), (258025.0, 258025.0, 0.0380096, 0.0, 3063.84)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(134664.0, 134664.0, 0.0376712, 0.0, 6953.06), (138622.0, 138622.0, 0.0363219, 0.0, 6988.71), (133456.0, 133456.0, 0.0380449, 0.0, 6924.66)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(312876.0, 312876.0, 0.0374476, 0.0, 4664.68), (313013.0, 313013.0, 0.0374199, 0.0, 4695.41), (309851.0, 309851.0, 0.0377907, 0.0, 4608.88)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(147921.0, 147921.0, 0.0373401, 0.0, 10293.3), (146719.0, 146719.0, 0.0376785, 0.0, 10249.4), (142295.0, 142295.0, 0.0391457, 0.0, 10189.7)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(414050.0, 414050.0, 0.0373039, 0.0, 9346.84), (397622.0, 397622.0, 0.0388512, 0.0, 8841.48), (390131.0, 390131.0, 0.0396171, 0.0, 8705.83)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(157425.0, 157425.0, 0.0376934, 0.0, 13473.3), (157162.0, 157162.0, 0.0377117, 0.0, 13501.3), (158112.0, 158112.0, 0.0374377, 0.0, 13528.2)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(487718.0, 487718.0, 0.0391512, 0.0, 14527.8), (476057.0, 476057.0, 0.040123, 0.0, 14131.1), (474003.0, 474003.0, 0.0403265, 0.0, 14046.5)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(167017.0, 167017.0, 0.0376897, 0.0, 16618.8), (167114.0, 167114.0, 0.0375454, 0.0, 16642.9), (167724.0, 167724.0, 0.0375093, 0.0, 16660.2)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(569716.0, 569716.0, 0.0397995, 0.0, 21317.7), (565332.0, 565332.0, 0.0401014, 0.0, 21155.6), (569637.0, 569637.0, 0.0398009, 0.0, 21278.9)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(176680.0, 176680.0, 0.0374522, 0.0, 19737.7), (176131.0, 176131.0, 0.0375693, 0.0, 19699.5), (169694.0, 169694.0, 0.0394857, 0.0, 19546.6)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(647802.0, 647802.0, 0.0403817, 0.0, 29106.6), (649554.0, 649554.0, 0.0402671, 0.0, 29253.2), (624850.0, 624850.0, 0.0418035, 0.0, 28084.9)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False}, [(177803.0, 177803.0, 0.0379713, 0.0, 21947.0), (176418.0, 176418.0, 0.0386506, 0.0, 22016.0), (176467.0, 176467.0, 0.0384451, 0.0, 21910.9)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(623901.0, 623901.0, 0.0461267, 0.0, 32038.7), (627919.0, 627919.0, 0.045893, 0.0, 32414.8), (633267.0, 633267.0, 0.0454382, 0.0, 32682.7)]), ({'binary': '../out-factor-gc-nowriteinplace/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(426155.0, 426155.0, 0.0655891, 0.0, 22.7828), (432210.0, 432210.0, 0.0646728, 0.0, 22.8162), (428042.0, 428042.0, 0.0652981, 0.0, 22.2329)]), ({'binary': '../out-factor-gc-nowriteinplace/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(483589.0, 483589.0, 0.0577953, 0.0, 25.666), (488737.0, 488737.0, 0.0571845, 0.0, 25.1995), (482652.0, 482652.0, 0.0579039, 0.0, 25.083)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(647104.0, 647104.0, 0.0431504, 0.0, 26.4822), (648314.0, 648314.0, 0.04308, 0.0, 27.3327), (650269.0, 650269.0, 0.0429486, 0.0, 27.4994)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(711287.0, 711287.0, 0.0392559, 0.0, 30.866), (720057.0, 720057.0, 0.0387759, 0.0, 33.7993), (713284.0, 713284.0, 0.0391468, 0.0, 32.6993)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': True, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(734030.0, 734030.0, 0.0380367, 0.0, 34.2159), (735049.0, 735049.0, 0.0379817, 0.0, 33.4993), (730100.0, 730100.0, 0.0382423, 0.0, 34.1326)]), ({'binary': '../out-factor-fake-compression/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(646630.0, 646630.0, 0.043147, 96.5089, 26.7218), (649321.0, 649321.0, 0.042985, 107.006, 26.9464), (650433.0, 650433.0, 0.0428896, 120.055, 27.0496)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': True}, [(562316.0, 562316.0, 0.0496007, 167.636, 21.647), (569509.0, 569509.0, 0.0490054, 184.241, 22.4489), (571327.0, 571327.0, 0.0488279, 191.067, 21.7078)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(184219.0, 184219.0, 0.0861123, 0.0, 2268.81), (186755.0, 186755.0, 0.0849474, 0.0, 2311.64), (186564.0, 186564.0, 0.0850164, 0.0, 2334.02)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(177982.0, 177982.0, 0.0891101, 0.0, 2300.09), (178695.0, 178695.0, 0.0887728, 0.0, 2261.79), (178712.0, 178712.0, 0.0887415, 0.0, 2336.5)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(175626.0, 175626.0, 0.0902572, 0.0, 2339.76), (174921.0, 174921.0, 0.0906454, 0.0, 2355.32), (176961.0, 176961.0, 0.0896144, 0.0, 2331.2)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(174207.0, 174207.0, 0.0909996, 0.0, 2314.15), (175069.0, 175069.0, 0.0905427, 0.0, 2380.53), (175155.0, 175155.0, 0.0905247, 0.0, 2357.35)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(173961.0, 173961.0, 0.0911228, 0.0, 2413.27), (173021.0, 173021.0, 0.0916247, 0.0, 2376.02), (173094.0, 173094.0, 0.0915701, 0.0, 2424.44)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(175126.0, 175126.0, 0.0905186, 0.0, 2413.01), (170861.0, 170861.0, 0.0927766, 0.0, 2320.99), (174009.0, 174009.0, 0.0910858, 0.0, 2433.45)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(156216.0, 156216.0, 0.0905651, 0.0, 14649.0), (158060.0, 158060.0, 0.0894652, 0.0, 14894.0), (157640.0, 157640.0, 0.0897404, 0.0, 14829.2)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(143294.0, 143294.0, 0.0969205, 0.0, 15430.2), (150768.0, 150768.0, 0.0923082, 0.0, 16068.1), (152628.0, 152628.0, 0.0911489, 0.0, 16330.1)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(148656.0, 148656.0, 0.0921969, 0.0, 17609.6), (147416.0, 147416.0, 0.093038, 0.0, 17435.0), (149081.0, 149081.0, 0.0919632, 0.0, 17680.3)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(145412.0, 145412.0, 0.0929773, 0.0, 18834.7), (143389.0, 143389.0, 0.0943072, 0.0, 18445.4), (146292.0, 146292.0, 0.0923443, 0.0, 18925.0)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(142789.0, 142789.0, 0.0934635, 0.0, 19946.9), (142389.0, 142389.0, 0.0937627, 0.0, 19858.4), (143424.0, 143424.0, 0.0930233, 0.0, 20007.1)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False}, [(141565.0, 141565.0, 0.0930924, 0.0, 21140.1), (140793.0, 140793.0, 0.0936215, 0.0, 20910.6), (141613.0, 141613.0, 0.0931092, 0.0, 21110.9)])]
diff --git a/silo/benchmarks/results/istc3-9-8-13.py b/silo/benchmarks/results/istc3-9-8-13.py
new file mode 100644 (file)
index 0000000..d0392c6
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(598829.0, 598829.0, 0.00162352, 0.0, 0.0), (576272.0, 576272.0, 0.00168956, 0.0, 0.0), (596995.0, 596995.0, 0.00162922, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(570013.0, 570013.0, 0.00170737, 0.0, 0.0), (576566.0, 576566.0, 0.00168789, 0.0, 0.0), (578566.0, 578566.0, 0.0016818, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 1, 'numa_memory': '42G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(575913.0, 575913.0, 0.00169019, 0.0, 0.0), (578635.0, 578635.0, 0.00168184, 0.0, 0.0), (574126.0, 574126.0, 0.00169477, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(2438130.0, 2438130.0, 0.00159217, 0.0, 0.0), (2431590.0, 2431590.0, 0.00159665, 0.0, 0.0), (2426370.0, 2426370.0, 0.00160015, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(2290490.0, 2290490.0, 0.00169747, 0.0, 0.0), (2291870.0, 2291870.0, 0.00169685, 0.0, 0.0), (2286970.0, 2286970.0, 0.00169992, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 4, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(2294240.0, 2294240.0, 0.00169429, 0.0, 0.0), (2291370.0, 2291370.0, 0.00169699, 0.0, 0.0), (2294750.0, 2294750.0, 0.00169438, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(4661400.0, 4661400.0, 0.00166712, 0.0, 0.0), (4687400.0, 4687400.0, 0.00165809, 0.0, 0.0), (4694850.0, 4694850.0, 0.00165542, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(4386430.0, 4386430.0, 0.00177481, 0.0, 0.0), (4379040.0, 4379040.0, 0.00177779, 0.0, 0.0), (4372870.0, 4372870.0, 0.00178038, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 8, 'numa_memory': '56G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(4408000.0, 4408000.0, 0.00176533, 0.0, 0.0), (4391850.0, 4391850.0, 0.00177218, 0.0, 0.0), (4382660.0, 4382660.0, 0.00177663, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(6466410.0, 6466410.0, 0.00180709, 0.0, 0.0), (6450240.0, 6450240.0, 0.00181201, 0.0, 0.0), (6476090.0, 6476090.0, 0.00180464, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(5903190.0, 5903190.0, 0.00198429, 0.0, 0.0166665), (5904180.0, 5904180.0, 0.00198394, 0.0, 0.0166664), (5909890.0, 5909890.0, 0.00198205, 0.0, 0.0166666)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 12, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(6177300.0, 6177300.0, 0.00189372, 0.0, 0.0333327), (6174910.0, 6174910.0, 0.00189447, 0.0, 0.0), (6163160.0, 6163160.0, 0.00189852, 0.0, 0.0333331)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(8401480.0, 8401480.0, 0.00185598, 0.0, 0.0), (8445120.0, 8445120.0, 0.00184608, 0.0, 0.0), (8428590.0, 8428590.0, 0.00184987, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(7891700.0, 7891700.0, 0.00197831, 0.0, 0.0333331), (7920470.0, 7920470.0, 0.00197126, 0.0, 0.0), (7924250.0, 7924250.0, 0.00196993, 0.0, 0.0166666)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 16, 'numa_memory': '72G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(8007260.0, 8007260.0, 0.00194911, 0.0, 0.0), (8050990.0, 8050990.0, 0.0019384, 0.0, 0.0), (7996460.0, 7996460.0, 0.00195181, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(10393900.0, 10393900.0, 0.00187588, 0.0, 0.0), (10085100.0, 10085100.0, 0.00193509, 0.0, 0.0), (10440000.0, 10440000.0, 0.00186743, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(9557570.0, 9557570.0, 0.00204384, 0.0, 0.0499995), (9538900.0, 9538900.0, 0.00204754, 0.0, 0.0), (9573950.0, 9573950.0, 0.00204017, 0.0, 0.0499993)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(9952310.0, 9952310.0, 0.00196109, 0.0, 0.0333329), (9972960.0, 9972960.0, 0.00195628, 0.0, 0.0166665), (9985650.0, 9985650.0, 0.001954, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(12298300.0, 12298300.0, 0.00190305, 0.0, 0.0), (12300300.0, 12300300.0, 0.00190267, 0.0, 0.0), (12312900.0, 12312900.0, 0.00190073, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(10579900.0, 10579900.0, 0.0022179, 0.0, 0.0333329), (10544700.0, 10544700.0, 0.0022271, 0.0, 0.0499995), (10558600.0, 10558600.0, 0.00222414, 0.0, 0.033333)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 24, 'numa_memory': '88G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(11803200.0, 11803200.0, 0.00198391, 0.0, 0.0166662), (11789800.0, 11789800.0, 0.00198707, 0.0, 0.0833317), (11782000.0, 11782000.0, 0.00198799, 0.0, 0.0833323)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(14403600.0, 14403600.0, 0.00189556, 0.0, 0.0), (14414900.0, 14414900.0, 0.00189406, 0.0, 0.0), (14447200.0, 14447200.0, 0.00188966, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(9598150.0, 9598150.0, 0.00286876, 0.0, 0.0499992), (9492480.0, 9492480.0, 0.00290082, 0.0, 0.0), (9400670.0, 9400670.0, 0.00292828, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 28, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(13783200.0, 13783200.0, 0.00198227, 0.0, 0.0), (13774500.0, 13774500.0, 0.00198424, 0.0, 0.0499994), (13753900.0, 13753900.0, 0.00198701, 0.0, 0.0499987)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'kvdb', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(16055600.0, 16055600.0, 0.00194448, 0.0, 0.0), (16089100.0, 16089100.0, 0.00194021, 0.0, 0.0), (16085200.0, 16085200.0, 0.00194084, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto1', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(8924990.0, 8924990.0, 0.00353644, 0.0, 0.0), (8876150.0, 8876150.0, 0.00355618, 0.0, 0.0), (8950210.0, 8950210.0, 0.00352647, 0.0, 0.066665)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 160000, 'name': 'scale_rmw', 'bench_opts': '--workload-mix 80,0,20,0', 'db': 'ndb-proto2', 'bench': 'ycsb', 'par_load': True, 'disable_gc': False, 'threads': 32, 'numa_memory': '104G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(15272000.0, 15272000.0, 0.00204642, 0.0, 0.0833321), (15297600.0, 15297600.0, 0.002041, 0.0, 0.0999969), (15355300.0, 15355300.0, 0.00203521, 0.0, 0.13333)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(28617.3, 28617.3, 0.0348511, 84.0693, 0.0), (28797.6, 28797.6, 0.0346053, 85.089, 0.0), (28634.6, 28634.6, 0.0348112, 82.1759, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(28599.0, 28599.0, 0.034848, 80.7266, 0.0), (29454.5, 29454.5, 0.0338453, 80.0298, 0.0), (29408.1, 29408.1, 0.0338889, 80.1761, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 1, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(31471.4, 31471.4, 0.0316881, 0.0, 0.0), (30873.7, 30873.7, 0.0323058, 0.0, 0.0), (31358.4, 31358.4, 0.0318088, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(102620.0, 102620.0, 0.0388521, 119.521, 4.29604), (102977.0, 102977.0, 0.0387193, 99.163, 4.03024), (102850.0, 102850.0, 0.0387698, 125.878, 4.11336)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(103526.0, 103526.0, 0.0385128, 95.7767, 3.97995), (103273.0, 103273.0, 0.0385991, 94.4058, 4.1124), (101679.0, 101679.0, 0.039207, 93.914, 3.92921)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(109121.0, 109121.0, 0.0365664, 0.0, 4.9333), (109623.0, 109623.0, 0.0363978, 0.0, 4.74996), (109268.0, 109268.0, 0.0365079, 0.0, 4.49996)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(193124.0, 193124.0, 0.0410664, 138.851, 8.42909), (191079.0, 191079.0, 0.0417271, 125.539, 7.87475), (191261.0, 191261.0, 0.041699, 140.306, 8.30992)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(196416.0, 196416.0, 0.0406024, 95.78, 7.51033), (194810.0, 194810.0, 0.0409333, 101.529, 7.94223), (195209.0, 195209.0, 0.0408526, 98.6565, 8.49267)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 8, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(210821.0, 210821.0, 0.0378565, 0.0, 10.0332), (207514.0, 207514.0, 0.0384576, 0.0, 8.74985), (208789.0, 208789.0, 0.0382243, 0.0, 8.74991)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(287716.0, 287716.0, 0.041558, 147.45, 11.1854), (284466.0, 284466.0, 0.0420381, 167.027, 11.4533), (284599.0, 284599.0, 0.0420073, 160.727, 12.0337)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(294521.0, 294521.0, 0.040619, 104.533, 12.1073), (294360.0, 294360.0, 0.0406423, 95.7708, 12.1238), (293600.0, 293600.0, 0.04075, 96.6426, 11.259)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 12, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(309782.0, 309782.0, 0.0386451, 0.0, 13.6312), (315285.0, 315285.0, 0.0379676, 0.0, 13.8332), (316796.0, 316796.0, 0.037787, 0.0, 13.4964)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(378445.0, 378445.0, 0.0421168, 197.735, 15.9599), (378523.0, 378523.0, 0.0421152, 243.092, 15.6954), (376749.0, 376749.0, 0.0423176, 146.844, 14.2827)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(384610.0, 384610.0, 0.0414841, 104.691, 15.2749), (383920.0, 383920.0, 0.0415515, 95.9765, 15.4567), (384704.0, 384704.0, 0.0414555, 105.822, 16.102)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 16, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(411151.0, 411151.0, 0.0388214, 0.0, 18.0664), (407373.0, 407373.0, 0.0391797, 0.0, 17.833), (405772.0, 405772.0, 0.0393358, 0.0, 16.6817)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(467720.0, 467720.0, 0.0425209, 285.225, 18.7369), (471138.0, 471138.0, 0.0423071, 180.023, 19.645), (470824.0, 470824.0, 0.0423446, 149.565, 18.4838)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(479899.0, 479899.0, 0.0415457, 108.423, 19.1495), (482061.0, 482061.0, 0.0413528, 107.488, 19.8966), (477598.0, 477598.0, 0.0417324, 106.076, 18.4774)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 20, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(518581.0, 518581.0, 0.0384662, 0.0, 23.7657), (521165.0, 521165.0, 0.0382815, 0.0, 22.2322), (520478.0, 520478.0, 0.0383316, 0.0, 21.9944)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(560251.0, 560251.0, 0.042649, 347.523, 22.7845), (557037.0, 557037.0, 0.0429351, 226.496, 22.6055), (566398.0, 566398.0, 0.0422298, 259.593, 22.5577)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(570554.0, 570554.0, 0.0419319, 109.373, 22.4786), (571235.0, 571235.0, 0.0418883, 109.831, 22.8149), (561708.0, 561708.0, 0.0425866, 109.558, 22.1268)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 24, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(614054.0, 614054.0, 0.0389887, 0.0, 26.9214), (612463.0, 612463.0, 0.0390935, 0.0, 25.5662), (611227.0, 611227.0, 0.0391734, 0.0, 26.1997)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(635704.0, 635704.0, 0.0432926, 440.427, 25.892), (641894.0, 641894.0, 0.0434858, 122.07, 25.8114), (636286.0, 636286.0, 0.043859, 129.47, 26.2711)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(645019.0, 645019.0, 0.0432778, 112.454, 25.5628), (644904.0, 644904.0, 0.0432636, 113.517, 26.0968), (648679.0, 648679.0, 0.0430153, 102.459, 25.6019)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(705610.0, 705610.0, 0.0395877, 0.0, 30.1652), (702785.0, 702785.0, 0.0397455, 0.0, 29.3329), (705719.0, 705719.0, 0.0395825, 0.0, 30.1161)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(699384.0, 699384.0, 0.0452612, 1136.85, 28.7525), (699960.0, 699960.0, 0.0452737, 1210.42, 29.2327), (696170.0, 696170.0, 0.0453715, 1474.05, 27.8344)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-temp', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(720767.0, 720767.0, 0.0442469, 759.988, 29.0968), (718494.0, 718494.0, 0.0443789, 648.494, 28.9776), (728285.0, 728285.0, 0.0437597, 773.536, 30.0091)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 32, 'name': 'scale_tpcc', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(814635.0, 814635.0, 0.0391764, 0.0, 35.1976), (823571.0, 823571.0, 0.0387536, 0.0, 37.2659), (813039.0, 813039.0, 0.0392549, 0.0, 36.2492)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(806288.0, 806288.0, 0.0346716, 0.0, 0.0), (812797.0, 812797.0, 0.0343964, 0.0, 0.0), (805790.0, 805790.0, 0.0346961, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(797902.0, 797902.0, 0.0350234, 0.0, 121.078), (788705.0, 788705.0, 0.0354296, 0.0, 116.181), (790483.0, 790483.0, 0.0353565, 0.0, 119.697)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(777488.0, 777488.0, 0.0359398, 0.0, 234.32), (793928.0, 793928.0, 0.0351839, 0.0, 238.275), (774937.0, 774937.0, 0.0360464, 0.0, 234.145)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(769009.0, 769009.0, 0.0363146, 0.0, 347.611), (781956.0, 781956.0, 0.0357226, 0.0, 349.994), (784384.0, 784384.0, 0.0356131, 0.0, 351.86)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(766040.0, 766040.0, 0.0364569, 0.0, 451.138), (774012.0, 774012.0, 0.0360733, 0.0, 467.057), (782923.0, 782923.0, 0.0356682, 0.0, 464.325)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(761386.0, 761386.0, 0.0366621, 0.0, 560.837), (773768.0, 773768.0, 0.0360849, 0.0, 571.139), (778537.0, 778537.0, 0.0358533, 0.0, 578.473)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(764569.0, 764569.0, 0.0365113, 0.0, 679.599), (759640.0, 759640.0, 0.0367474, 0.0, 666.214), (776602.0, 776602.0, 0.0359345, 0.0, 687.29)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(765354.0, 765354.0, 0.036465, 0.0, 773.979), (760492.0, 760492.0, 0.0366901, 0.0, 780.935), (765215.0, 765215.0, 0.0364707, 0.0, 779.952)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(741889.0, 741889.0, 0.0376049, 0.0, 862.211), (765299.0, 765299.0, 0.0364518, 0.0, 895.632), (767722.0, 767722.0, 0.0363191, 0.0, 902.566)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(751384.0, 751384.0, 0.0371292, 0.0, 985.814), (763935.0, 763935.0, 0.0365074, 0.0, 1001.3), (756551.0, 756551.0, 0.0368641, 0.0, 999.711)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(761211.0, 761211.0, 0.0366384, 0.0, 1092.68), (763319.0, 763319.0, 0.0365279, 0.0, 1110.38), (765116.0, 765116.0, 0.0364423, 0.0, 1102.56)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(825461.0, 825461.0, 0.033858, 0.0, 0.0), (815822.0, 815822.0, 0.0342369, 0.0, 0.0), (821089.0, 821089.0, 0.0340414, 0.0, 0.0)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(811573.0, 811573.0, 0.034428, 0.0, 121.597), (814350.0, 814350.0, 0.034308, 0.0, 124.348), (816024.0, 816024.0, 0.0342422, 0.0, 125.231)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(808929.0, 808929.0, 0.0345328, 0.0, 245.946), (799773.0, 799773.0, 0.0349224, 0.0, 242.943), (790461.0, 790461.0, 0.0353392, 0.0, 238.913)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(804244.0, 804244.0, 0.0347248, 0.0, 367.843), (799706.0, 799706.0, 0.0349187, 0.0, 362.959), (796449.0, 796449.0, 0.0350647, 0.0, 361.86)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(794453.0, 794453.0, 0.0351407, 0.0, 487.235), (788685.0, 788685.0, 0.0353989, 0.0, 476.802), (787245.0, 787245.0, 0.0354657, 0.0, 475.537)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(798494.0, 798494.0, 0.0349548, 0.0, 602.005), (800584.0, 800584.0, 0.034865, 0.0, 608.673), (788170.0, 788170.0, 0.0354135, 0.0, 596.038)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(794715.0, 794715.0, 0.0351173, 0.0, 712.204), (782944.0, 782944.0, 0.0355952, 0.0, 699.92), (782979.0, 782979.0, 0.0356386, 0.0, 705.519)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(765775.0, 765775.0, 0.0364354, 0.0, 804.152), (792340.0, 792340.0, 0.0352126, 0.0, 829.994), (785538.0, 785538.0, 0.0355196, 0.0, 827.851)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(769798.0, 769798.0, 0.0362347, 0.0, 913.05), (783104.0, 783104.0, 0.0356187, 0.0, 938.75), (790762.0, 790762.0, 0.035276, 0.0, 940.525)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(774841.0, 774841.0, 0.0359907, 0.0, 1027.7), (776136.0, 776136.0, 0.0359342, 0.0, 1026.0), (781545.0, 781545.0, 0.0356814, 0.0, 1041.08)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(785045.0, 785045.0, 0.0355166, 0.0, 1159.89), (781998.0, 781998.0, 0.0356545, 0.0, 1146.56), (775092.0, 775092.0, 0.0359774, 0.0, 1136.1)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(925456.0, 925456.0, 0.0301944, 0.0, 0.0), (924710.0, 924710.0, 0.0302244, 0.0, 0.0), (912381.0, 912381.0, 0.0306279, 0.0, 0.0)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 1', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(905686.0, 905686.0, 0.030846, 0.0, 139.797), (923047.0, 923047.0, 0.0302669, 0.0, 141.698), (912168.0, 912168.0, 0.0306273, 0.0, 140.031)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 2', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(915460.0, 915460.0, 0.0305097, 0.0, 278.892), (891186.0, 891186.0, 0.0313397, 0.0, 272.795), (910633.0, 910633.0, 0.0306712, 0.0, 281.36)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 3', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(909618.0, 909618.0, 0.0306986, 0.0, 409.575), (900989.0, 900989.0, 0.0309938, 0.0, 405.692), (909806.0, 909806.0, 0.0306935, 0.0, 413.409)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 4', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(892349.0, 892349.0, 0.031283, 0.0, 543.606), (895733.0, 895733.0, 0.0311659, 0.0, 534.007), (905308.0, 905308.0, 0.0308356, 0.0, 546.89)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 5', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(892090.0, 892090.0, 0.0312859, 0.0, 669.004), (883095.0, 883095.0, 0.0316099, 0.0, 663.537), (892337.0, 892337.0, 0.0312792, 0.0, 658.321)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 6', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(881039.0, 881039.0, 0.0316704, 0.0, 793.076), (882058.0, 882058.0, 0.0316328, 0.0, 784.9), (880395.0, 880395.0, 0.0316974, 0.0, 782.47)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 7', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(886761.0, 886761.0, 0.0314644, 0.0, 921.532), (881661.0, 881661.0, 0.0316401, 0.0, 915.601), (882964.0, 882964.0, 0.0315967, 0.0, 917.951)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 8', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(880621.0, 880621.0, 0.0316745, 0.0, 1031.19), (871377.0, 871377.0, 0.0320079, 0.0, 1025.53), (876721.0, 876721.0, 0.0318137, 0.0, 1034.05)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 9', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(881304.0, 881304.0, 0.0316413, 0.0, 1159.76), (869172.0, 869172.0, 0.0320832, 0.0, 1144.78), (886523.0, 886523.0, 0.0314546, 0.0, 1169.68)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct 10', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(863583.0, 863583.0, 0.0322865, 0.0, 1254.06), (878930.0, 878930.0, 0.0317189, 0.0, 1276.02), (877821.0, 877821.0, 0.0317597, 0.0, 1278.09)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 0', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1242960.0, 1242960.0, 0.0224758, 0.0, 0.0), (1247870.0, 1247870.0, 0.0223908, 0.0, 0.0), (1227620.0, 1227620.0, 0.0227595, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 1', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(1043580.0, 1043580.0, 0.0267812, 0.0, 0.0), (1039090.0, 1039090.0, 0.0268949, 0.0, 0.0), (1032500.0, 1032500.0, 0.0270694, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 2', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(855396.0, 855396.0, 0.0326825, 0.0, 0.0), (860291.0, 860291.0, 0.0324972, 0.0, 0.0), (855942.0, 855942.0, 0.032662, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 3', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(704703.0, 704703.0, 0.0396825, 0.0, 0.0), (695359.0, 695359.0, 0.0402165, 0.0, 0.0), (698044.0, 698044.0, 0.0400605, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 4', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(591127.0, 591127.0, 0.0473125, 0.0, 0.0), (586776.0, 586776.0, 0.0476642, 0.0, 0.0), (580026.0, 580026.0, 0.0482227, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 5', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(478630.0, 478630.0, 0.0584464, 0.0, 0.0), (491725.0, 491725.0, 0.0568888, 0.0, 0.0), (493271.0, 493271.0, 0.0567128, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 6', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(408654.0, 408654.0, 0.0684639, 0.0, 0.0), (419374.0, 419374.0, 0.0667132, 0.0, 0.0), (419056.0, 419056.0, 0.0667628, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 7', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(359188.0, 359188.0, 0.0778998, 0.0, 0.0), (359580.0, 359580.0, 0.0778133, 0.0, 0.0), (349631.0, 349631.0, 0.0800309, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 8', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(319971.0, 319971.0, 0.0874537, 0.0, 0.0), (319187.0, 319187.0, 0.0876687, 0.0, 0.0), (310965.0, 310965.0, 0.0899824, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 9', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(284031.0, 284031.0, 0.098525, 0.0, 0.0), (280539.0, 280539.0, 0.0997519, 0.0, 0.0), (284378.0, 284378.0, 0.0984049, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'multipart:pct', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct 10', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(255585.0, 255585.0, 0.109496, 0.0, 0.0), (256025.0, 256025.0, 0.109306, 0.0, 0.0), (256373.0, 256373.0, 0.109162, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks', 'db': 'kvdb-st', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(44495.0, 44495.0, 0.0224293, 0.0, 0.0), (44288.4, 44288.4, 0.0225344, 0.0, 0.0), (44321.1, 44321.1, 0.0225171, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(32118.3, 32118.3, 0.031069, 0.0, 0.0), (31969.4, 31969.4, 0.0312214, 0.0, 0.0), (31833.7, 31833.7, 0.0313521, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 1, 'numa_memory': '4G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(32951.1, 32951.1, 0.0302875, 0.0, 0.0), (32419.2, 32419.2, 0.0307953, 0.0, 0.0), (32893.5, 32893.5, 0.0303424, 0.0, 0.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': '8G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(62796.0, 62796.0, 0.0317857, 0.0, 3.31664), (62559.5, 62559.5, 0.0319149, 0.0, 3.14998), (61874.2, 61874.2, 0.0322658, 0.0, 2.99998)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 2, 'numa_memory': '8G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(64657.0, 64657.0, 0.0308702, 0.0, 3.28332), (64476.1, 64476.1, 0.0309545, 0.0, 3.48331), (64139.1, 64139.1, 0.0311286, 0.0, 3.26665)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(121387.0, 121387.0, 0.032889, 0.0, 18.0999), (121454.0, 121454.0, 0.0328622, 0.0, 18.0498), (122034.0, 122034.0, 0.0327109, 0.0, 19.4165)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 4, 'numa_memory': '16G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(123894.0, 123894.0, 0.0322137, 0.0, 19.0832), (123991.0, 123991.0, 0.0321967, 0.0, 18.9832), (124994.0, 124994.0, 0.0319308, 0.0, 19.2999)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': '24G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(169515.0, 169515.0, 0.0327435, 0.0, 11079.4), (168722.0, 168722.0, 0.0329019, 0.0, 11018.7), (168456.0, 168456.0, 0.0329514, 0.0, 11011.8)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 6, 'numa_memory': '24G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(183716.0, 183716.0, 0.0323312, 0.0, 956.825), (183498.0, 183498.0, 0.0323662, 0.0, 960.558), (182594.0, 182594.0, 0.0325335, 0.0, 949.625)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(216280.0, 216280.0, 0.0328513, 0.0, 22023.0), (210393.0, 210393.0, 0.0338061, 0.0, 21584.0), (213755.0, 213755.0, 0.0332433, 0.0, 21779.1)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 8, 'numa_memory': '32G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(238839.0, 238839.0, 0.0330196, 0.0, 1873.42), (237640.0, 237640.0, 0.0331984, 0.0, 1869.13), (239380.0, 239380.0, 0.0329553, 0.0, 1877.23)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': '40G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(250519.0, 250519.0, 0.0330805, 0.0, 40559.1), (241457.0, 241457.0, 0.0343597, 0.0, 39833.8), (250890.0, 250890.0, 0.0330292, 0.0, 40774.7)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 10, 'numa_memory': '40G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(293347.0, 293347.0, 0.0333821, 0.0, 3590.83), (295378.0, 295378.0, 0.0331443, 0.0, 3610.9), (292020.0, 292020.0, 0.0335388, 0.0, 3585.19)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(275702.0, 275702.0, 0.0344281, 0.0, 57568.0), (277685.0, 277685.0, 0.0342305, 0.0, 57495.8), (279760.0, 279760.0, 0.0339387, 0.0, 58130.7)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 12, 'numa_memory': '48G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(343407.0, 343407.0, 0.0340644, 0.0, 5269.62), (340971.0, 340971.0, 0.0343138, 0.0, 5212.04), (341800.0, 341800.0, 0.034232, 0.0, 5232.51)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(327565.0, 327565.0, 0.0345718, 0.0, 103537.0), (325068.0, 325068.0, 0.0347459, 0.0, 103584.0), (327822.0, 327822.0, 0.0344385, 0.0, 104351.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(432829.0, 432829.0, 0.035626, 0.0, 9930.59), (442102.0, 442102.0, 0.0348777, 0.0, 10143.6), (441455.0, 441455.0, 0.0349215, 0.0, 10166.3)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(352584.0, 352584.0, 0.0352099, 0.0, 150022.0), (353061.0, 353061.0, 0.034983, 0.0, 150998.0), (349523.0, 349523.0, 0.0353466, 0.0, 150234.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 20, 'numa_memory': '80G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(531869.0, 531869.0, 0.0358097, 0.0, 16305.2), (531126.0, 531126.0, 0.0358588, 0.0, 16329.8), (531405.0, 531405.0, 0.0358489, 0.0, 16244.1)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(362583.0, 362583.0, 0.0353427, 0.0, 191307.0), (363862.0, 363862.0, 0.0353339, 0.0, 191715.0), (363285.0, 363285.0, 0.0353368, 0.0, 191112.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 24, 'numa_memory': '96G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(618380.0, 618380.0, 0.0365441, 0.0, 23631.0), (621098.0, 621098.0, 0.0363649, 0.0, 23789.4), (619742.0, 619742.0, 0.0364553, 0.0, 23719.6)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(338537.0, 338537.0, 0.0365146, 0.0, 205308.0), (337417.0, 337417.0, 0.0363902, 0.0, 207241.0), (336938.0, 336938.0, 0.03607, 0.0, 207617.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(690271.0, 690271.0, 0.0377613, 0.0, 31631.7), (695852.0, 695852.0, 0.037431, 0.0, 32018.7), (695134.0, 695134.0, 0.0374861, 0.0, 31971.2)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': True, 'log_compress': False, 'disable_madv_willneed': True}, [(300664.0, 300664.0, 0.0364553, 0.0, 195246.0), (309101.0, 309101.0, 0.0362933, 0.0, 201725.0), (307603.0, 307603.0, 0.0365383, 0.0, 200124.0)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 4, 'name': 'multipart:skew', 'bench_opts': '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 32, 'numa_memory': '128G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(762910.0, 762910.0, 0.0379692, 0.0, 40435.0), (766111.0, 766111.0, 0.0378948, 0.0, 40718.0), (753796.0, 753796.0, 0.0385245, 0.0, 39840.5)]), ({'binary': '../out-factor-gc-nowriteinplace/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': None, 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(433200.0, 433200.0, 0.0645148, 0.0, 23.2829), (429709.0, 429709.0, 0.0650403, 0.0, 22.1329), (432455.0, 432455.0, 0.0646299, 0.0, 22.9662)]), ({'binary': '../out-factor-gc-nowriteinplace/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(525665.0, 525665.0, 0.0531658, 0.0, 26.2329), (520951.0, 520951.0, 0.0536426, 0.0, 28.9828), (522117.0, 522117.0, 0.0535238, 0.0, 27.6495)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(698023.0, 698023.0, 0.0400015, 0.0, 30.2117), (702420.0, 702420.0, 0.0397585, 0.0, 31.3994), (699132.0, 699132.0, 0.0399416, 0.0, 30.7157)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--disable-read-only-snapshots', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(759458.0, 759458.0, 0.0367641, 0.0, 99.8045), (769139.0, 769139.0, 0.0362966, 0.0, 103.031), (759587.0, 759587.0, 0.0367557, 0.0, 99.7311)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'factoranalysis', 'bench_opts': '--disable-read-only-snapshots', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': True, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(777304.0, 777304.0, 0.035915, 0.0, 102.848), (779015.0, 779015.0, 0.0358351, 0.0, 105.114), (777038.0, 777038.0, 0.0359254, 0.0, 104.731)]), ({'binary': '../out-factor-fake-compression/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(688122.0, 688122.0, 0.0405507, 105.366, 30.9495), (685386.0, 685386.0, 0.0407172, 96.5505, 29.9872), (690778.0, 690778.0, 0.0404074, 105.092, 30.0936)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': False, 'scale_factor': 28, 'name': 'persistfactoranalysis', 'bench_opts': '', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 28, 'numa_memory': '112G', 'persist': 'persist-real', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': True, 'disable_madv_willneed': True}, [(596712.0, 596712.0, 0.046765, 139.349, 23.7017), (600256.0, 600256.0, 0.0464942, 145.855, 22.8553), (603010.0, 603010.0, 0.0462962, 147.18, 23.5958)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(199646.0, 199646.0, 0.0794695, 0.0, 2514.41), (205182.0, 205182.0, 0.0773246, 0.0, 2603.53), (200252.0, 200252.0, 0.0792314, 0.0, 2570.28)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(197077.0, 197077.0, 0.0805048, 0.0, 2514.27), (196762.0, 196762.0, 0.0806183, 0.0, 2570.83), (196422.0, 196422.0, 0.0807498, 0.0, 2611.23)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(196410.0, 196410.0, 0.0807394, 0.0, 2658.45), (196638.0, 196638.0, 0.0806373, 0.0, 2659.57), (195049.0, 195049.0, 0.0813108, 0.0, 2631.86)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(194832.0, 194832.0, 0.0813761, 0.0, 2682.3), (192620.0, 192620.0, 0.0823341, 0.0, 2604.41), (193308.0, 193308.0, 0.0820224, 0.0, 2643.47)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(194833.0, 194833.0, 0.0813758, 0.0, 2662.9), (194696.0, 194696.0, 0.081434, 0.0, 2695.99), (194545.0, 194545.0, 0.0814888, 0.0, 2691.61)]), ({'binary': '../out-perf/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': False, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(194546.0, 194546.0, 0.0815033, 0.0, 2684.96), (192913.0, 192913.0, 0.0821921, 0.0, 2649.91), (194385.0, 194385.0, 0.0815586, 0.0, 2688.2)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 0', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(168978.0, 168978.0, 0.0837348, 0.0, 15890.0), (161929.0, 161929.0, 0.0872612, 0.0, 15326.3), (168062.0, 168062.0, 0.0842055, 0.0, 15755.6)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 20', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(164062.0, 164062.0, 0.0848855, 0.0, 17470.9), (164666.0, 164666.0, 0.0845902, 0.0, 17461.6), (161672.0, 161672.0, 0.0862109, 0.0, 17059.7)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 40', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(158790.0, 158790.0, 0.0864914, 0.0, 18601.4), (157279.0, 157279.0, 0.0872426, 0.0, 18473.4), (160467.0, 160467.0, 0.0855206, 0.0, 18853.0)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 60', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(157723.0, 157723.0, 0.0858837, 0.0, 20168.6), (157928.0, 157928.0, 0.0857274, 0.0, 20283.9), (157018.0, 157018.0, 0.0862519, 0.0, 20123.3)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 80', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(155544.0, 155544.0, 0.0859683, 0.0, 21492.7), (154378.0, 154378.0, 0.0866151, 0.0, 21396.8), (154077.0, 154077.0, 0.0868223, 0.0, 21207.4)]), ({'binary': '../out-factor-gc/benchmarks/dbtest', 'log_fake_writes': False, 'retry': True, 'scale_factor': 8, 'name': 'readonly', 'bench_opts': '--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct 100', 'db': 'ndb-proto2', 'bench': 'tpcc', 'par_load': False, 'disable_gc': False, 'threads': 16, 'numa_memory': '64G', 'persist': 'persist-none', 'disable_snapshots': True, 'log_nofsync': False, 'backoff': False, 'log_compress': False, 'disable_madv_willneed': True}, [(150274.0, 150274.0, 0.0879101, 0.0, 22111.7), (153706.0, 153706.0, 0.0859128, 0.0, 22644.0), (151011.0, 151011.0, 0.0874784, 0.0, 22178.2)])]
diff --git a/silo/benchmarks/results/make_graphs-2.py b/silo/benchmarks/results/make_graphs-2.py
new file mode 100755 (executable)
index 0000000..6b2e141
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+import matplotlib
+import pylab as plt
+import numpy as np
+
+import os
+import sys
+
+if __name__ == '__main__':
+  files = sys.argv[1:]
+  for f in files:
+    execfile(f)
+    for db in DBS:
+      values = []
+      for t in THREADS:
+        # find config in results...
+        V = None
+        for (r, v) in RESULTS:
+          if r['db'] == db and r['threads'] == t:
+            V = v
+            break
+        assert V
+        values.append(V)
+      plt.plot(THREADS, values)
+    #for (db, res) in zip(DBS[:-1], RESULTS):
+    #  #plt.plot(THREADS, np.log(np.array(res)))
+    #  #plt.plot(THREADS, res)
+    #  #plt.plot(THREADS, np.array(res)/np.array(THREADS)) # per-core
+    #  plt.plot(THREADS, np.log10(np.array(res)/np.array(THREADS))) # per-core
+    plt.xlabel('num threads')
+    plt.ylabel('txns/sec')
+    #plt.ylabel('ops/sec/core')
+    #plt.ylabel('$log_{10}$ ops/sec/thread')
+    plt.legend(DBS, loc='right')
+    plt.title('TPCC workload scale factor 10')
+    plt.savefig('.'.join(os.path.basename(f).split('.')[:-1] + ['pdf']))
+    plt.close()
diff --git a/silo/benchmarks/results/make_graphs-3.py b/silo/benchmarks/results/make_graphs-3.py
new file mode 100755 (executable)
index 0000000..b482606
--- /dev/null
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+
+import matplotlib
+import pylab as plt
+import numpy as np
+
+import os
+import sys
+import math
+
+if __name__ == '__main__':
+  files = sys.argv[1:]
+  for f in files:
+    execfile(f)
+
+    #names = ['scale', 'multipart:pct', 'multipart:cpu']
+
+    def deal_with_posK_res(k, x):
+      if type(x) == list:
+        return [e[k] for e in x]
+      return x[k]
+
+    def deal_with_pos0_res(x):
+      return deal_with_posK_res(0, x)
+
+    def deal_with_pos1_res(x):
+      return deal_with_posK_res(1, x)
+
+    import re
+    RGX = re.compile(r'--new-order-remote-item-pct (\d+)')
+    def extract_pct(x):
+      m = RGX.search(x)
+      assert m
+      p = int(m.group(1))
+      assert p >= 0 and p <= 100
+      def pn(n, p):
+        return 1.0 - (1.0 - p)**n
+      def ex(p):
+        import math
+        return math.fsum([(1.0/11.0)*pn(float(n), p) for n in range(5, 16)])
+      return ex(p/100.0) * 100.0
+
+    def extract_p(x):
+      m = RGX.search(x)
+      assert m
+      p = int(m.group(1))
+      assert p >= 0 and p <= 100
+      return p
+
+    def multipart_cpu_process(config):
+      assert config['db'] == 'ndb-proto2' or \
+             config['db'] == 'kvdb'
+      if config['db'] == 'ndb-proto2':
+        return config['threads']
+      else:
+        return 8
+
+    def readonly_lines_func(config):
+      if 'disable-read-only-snapshots' in config['bench_opts']:
+        return 'No-Snapshots'
+      else:
+        return 'Snapshots'
+
+    def MFormatter(x, p):
+      if x == 0:
+        return '0'
+      v = float(x)/float(10**6)
+      if math.ceil(v) == v:
+        return '%dM' % v
+      return '%.1fM' % v
+
+    def KFormatter(x, p):
+      if x == 0:
+        return '0'
+      v = float(x)/float(10**3)
+      if math.ceil(v) == v:
+        return '%dK' % v
+      return '%.1fK' % v
+
+    descs = [
+      {
+        'name' : 'scale',
+        'x-axis' : 'threads',
+        'x-axis-func' : lambda x: x,
+        'y-axis' : deal_with_pos0_res,
+        'lines' : ['db'], # each line holds this constant
+        'x-label' : 'threads',
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(MFormatter),
+        'x-axis-set-major-locator' : True,
+        #'title' : 'ycsb throughput graph',
+      },
+      {
+        'name' : 'scale_tpcc',
+        'x-axis' : 'threads',
+        'x-axis-func' : lambda x: x,
+        'y-axis' : deal_with_pos0_res,
+        'lines' : ['db'], # each line holds this constant
+        'x-label' : 'threads',
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(MFormatter),
+        'x-axis-set-major-locator' : True,
+        #'title' : 'tpcc throughput graph',
+      },
+      {
+        'name' : 'multipart:pct',
+        'x-axis' : 'bench_opts',
+        'x-axis-func' : extract_pct,
+        'y-axis' : deal_with_pos0_res,
+        'lines' : ['db'], # each line holds this constant
+        'x-label' : '% cross-partition',
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(MFormatter),
+        'x-axis-set-major-locator' : False,
+        #'title' : 'tpcc new-order throughput graph',
+        'legend' : 'upper right',
+      },
+      #{
+      #  'name' : 'multipart:cpu',
+      #  'x-axis-process' : multipart_cpu_process,
+      #  'y-axis' : deal_with_pos0_res,
+      #  'lines' : ['db'], # each line holds this constant
+      #  'x-label' : 'num threads',
+      #  'y-label' : 'txns/sec',
+      #  'title' : 'tpcc full workload throughput graph',
+      #},
+      {
+        'name' : 'readonly',
+        'x-axis' : 'bench_opts',
+        'x-axis-func' : extract_p,
+        'y-axis' : deal_with_pos0_res,
+        'lines-func' : readonly_lines_func,
+        'x-label' : '% remote warehouse stock',
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(KFormatter),
+        'x-axis-set-major-locator' : True,
+        #'title' : 'tpcc read only throughput graph',
+        'legend' : 'right',
+      },
+    ]
+
+    def label_transform(x):
+      if x == 'kvdb':
+        return 'Key-Value'
+      if x == 'ndb-proto1':
+        return 'Malflingo-Star'
+      if x == 'ndb-proto2':
+        return 'Malflingo'
+      if x == 'kvdb-st':
+        return 'Partitioned-Store'
+      return x
+
+    for desc in descs:
+      bench = desc['name']
+      bench_results = [d for d in RESULTS if d[0]['name'] == bench]
+      if not bench_results:
+        print >>sys.stderr, 'skipping bench %s' % bench
+        continue
+      lines = {}
+      for (config, result) in bench_results:
+        if 'lines-func' in desc:
+          key = desc['lines-func'](config)
+        else:
+          key = tuple(config[x] for x in desc['lines'])
+        pts = lines.get(key, {})
+        if 'x-axis-process' in desc:
+          xpt = desc['x-axis-process'](config)
+        else:
+          xpt = desc['x-axis-func'](config[desc['x-axis']])
+        assert xpt not in pts
+        pts[xpt] = desc['y-axis'](result)
+        lines[key] = pts
+
+      def mean(x): return sum(x)/len(x)
+      def median(x): return sorted(x)[len(x)/2]
+
+      # find min/max of xpts
+      xmin = min([e for l in lines.values() for e in l])
+      xmax = max([e for l in lines.values() for e in l])
+      #print xmin, xmax
+
+      labels = []
+      for (name, pts) in lines.iteritems():
+        spts = sorted(pts.iteritems(), key=lambda x: x[0])
+        ypts = [sorted(x[1]) for x in spts]
+        ymins = np.array([min(x) for x in ypts])
+        ymaxs = np.array([max(x) for x in ypts])
+        ymid = np.array([median(x) for x in ypts])
+        yerr=np.array([ymid - ymins, ymaxs - ymid])
+        xpts = [x[0] for x in spts]
+        assert len(xpts)
+        if len(xpts) == 1:
+          xpts = range(xmin, xmax + 1)
+          assert len(ymins) == 1
+          assert len(ymaxs) == 1
+          assert len(ymid) == 1
+          ymins = np.array([ymins[0] for _ in xpts])
+          ymaxs = np.array([ymaxs[0] for _ in xpts])
+          ymid = np.array([ymid[0] for _ in xpts])
+          yerr=np.array([ymid - ymins, ymaxs - ymid])
+
+        plt.errorbar(xpts, ymid, yerr=yerr)
+        if type(name) == str:
+          labels.append(label_transform(name))
+        else:
+          labels.append(label_transform('-'.join(name)))
+
+      ax = plt.gca()
+      if desc['x-axis-set-major-locator']:
+        ax.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(sorted(lines.values()[0].keys())))
+      if 'y-axis-major-formatter' in desc:
+        ax.yaxis.set_major_formatter(desc['y-axis-major-formatter'])
+
+      plt.xlabel(desc['x-label'])
+      plt.ylabel(desc['y-label'])
+      if 'title' in desc:
+        plt.title(desc['title'])
+
+      plt.xlim(xmin = xmin, xmax = xmax)
+      plt.ylim(ymin = 0)
+
+      placement = 'upper left' if not 'legend' in desc else desc['legend']
+      plt.legend(labels, loc=placement)
+      bname = '.'.join(os.path.basename(f).split('.')[:-1])
+      plt.savefig('.'.join([bname + '-' + bench, 'pdf']))
+      plt.close()
diff --git a/silo/benchmarks/results/make_graphs-4.py b/silo/benchmarks/results/make_graphs-4.py
new file mode 100644 (file)
index 0000000..86acae7
--- /dev/null
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+
+import matplotlib
+import pylab as plt
+import numpy as np
+
+import os
+import sys
+import math
+
+def filter_name(results, name):
+  def match(ent):
+    return ent[0]['name'] == name
+  return [x for x in results if match(x)]
+
+def order_results_by_threads(results):
+  # res is list[(config, results)], change to
+  # list[(num_threads, results)]
+  def trfm(ent):
+    return (ent[0]['threads'], ent[1])
+  return map(trfm, results)
+
+def split_results_by_predicate(results, pred):
+  s0, s1 = [], []
+  for res in results:
+    if pred(res):
+      s0.append(res)
+    else:
+      s1.append(res)
+  return s0, s1
+
+def extract_result_position(k, res):
+  if type(res) == list:
+    return [x[k] for x in res]
+  return res[k]
+
+def extract_throughput(results, persist):
+  def trfm(ent):
+    return (ent[0], extract_result_position(0 if not persist else 1, ent[1]))
+  return map(trfm, results)
+
+def extract_latency(results, persist):
+  def trfm(ent):
+    return (ent[0], extract_result_position(2 if not persist else 3, ent[1]))
+  return map(trfm, results)
+
+def XX(x):
+  return [e[0] for e in x]
+
+def median(x): return sorted(x)[len(x)/2]
+
+def YY(x):
+  def checked(e):
+    if type(e) == list:
+      return median(e)
+    return e
+  return [checked(e[1]) for e in x]
+
+def YYPC(x):
+  def checked(e):
+    if type(e) == list:
+      return median(e)
+    return e
+  return [checked(e[1])/float(e[0]) for e in x]
+
+def YERR(x):
+  ypts = [e[1] for e in x]
+  ymins = np.array([min(x) for x in ypts])
+  ymaxs = np.array([max(x) for x in ypts])
+  ymid = np.array([median(x) for x in ypts])
+  yerr=np.array([ymid - ymins, ymaxs - ymid])
+  return yerr
+
+def YERRPC(x):
+  ypts = [[ee/float(e[0]) for ee in e[1]] for e in x]
+  ymins = np.array([min(x) for x in ypts])
+  ymaxs = np.array([max(x) for x in ypts])
+  ymid = np.array([median(x) for x in ypts])
+  yerr=np.array([ymid - ymins, ymaxs - ymid])
+  return yerr
+
+def handle_scale_tpcc(f, results):
+  # two graphs
+  # x-axis is num threads on both
+  # y-axis[0] is throughput
+  # y-axis[1] is latency
+
+  no_persist, with_persist = \
+      split_results_by_predicate(results, lambda x: not x[0]['persist'])
+  no_persist, with_persist = \
+      order_results_by_threads(no_persist), order_results_by_threads(with_persist)
+
+  no_persist_throughput, no_persist_latency = \
+      extract_throughput(no_persist, False), extract_latency(no_persist, False)
+  with_persist_throughput, with_persist_latency = \
+      extract_throughput(with_persist, True), extract_latency(with_persist, True)
+
+  fig = plt.figure()
+  ax = plt.subplot(111)
+  ax.errorbar(XX(no_persist_throughput), YY(no_persist_throughput), yerr=YERR(no_persist_throughput))
+  ax.errorbar(XX(with_persist_throughput), YY(with_persist_throughput), yerr=YERR(with_persist_throughput))
+  ax.legend(('No-Persist', 'Persist'), loc='upper left')
+  ax.set_xlabel('threads')
+  ax.set_ylabel('throughput (txns/sec)')
+  bname = '.'.join(os.path.basename(f).split('.')[:-1])
+  fig.savefig('.'.join([bname + '-scale_tpcc-throughput', 'pdf']))
+
+  fig = plt.figure()
+  ax = plt.subplot(111)
+  ax.errorbar(XX(no_persist_throughput), YYPC(no_persist_throughput), yerr=YERRPC(no_persist_throughput))
+  ax.errorbar(XX(with_persist_throughput), YYPC(with_persist_throughput), yerr=YERRPC(with_persist_throughput))
+  ax.legend(('No-Persist', 'Persist'), loc='upper left')
+  ax.set_xlabel('threads')
+  ax.set_ylabel('throughput (txns/sec/core)')
+  ax.set_ylim([20000, 32000])
+  bname = '.'.join(os.path.basename(f).split('.')[:-1])
+  fig.savefig('.'.join([bname + '-scale_tpcc-per-core-throughput', 'pdf']))
+
+  fig = plt.figure()
+  ax = plt.subplot(111)
+  ax.errorbar(XX(no_persist_latency), YY(no_persist_latency), yerr=YERR(no_persist_latency))
+  ax.errorbar(XX(with_persist_latency), YY(with_persist_latency), yerr=YERR(with_persist_latency))
+  ax.legend(('No-Persist', 'Persist'), loc='upper left')
+  ax.set_xlabel('threads')
+  ax.set_ylabel('latency (ms/txn)')
+  bname = '.'.join(os.path.basename(f).split('.')[:-1])
+  fig.savefig('.'.join([bname + '-scale_tpcc-latency', 'pdf']))
+
+if __name__ == '__main__':
+  files = sys.argv[1:]
+  for f in files:
+    execfile(f)
+
+    # scale_tpcc
+    scale_tpcc = filter_name(RESULTS, 'scale_tpcc')
+    if scale_tpcc:
+      handle_scale_tpcc(f, scale_tpcc)
diff --git a/silo/benchmarks/results/make_graphs-5.py b/silo/benchmarks/results/make_graphs-5.py
new file mode 100644 (file)
index 0000000..1abe6b9
--- /dev/null
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+
+import matplotlib
+import pylab as plt
+import numpy as np
+
+import os
+import sys
+import math
+
+NAMEPAT='istc3-8-1-13%s.py'
+def N(x):
+    return NAMEPAT % x
+
+def split_results_by_predicate(results, pred):
+  s0, s1 = [], []
+  for res in results:
+    if pred(res):
+      s0.append(res)
+    else:
+      s1.append(res)
+  return s0, s1
+
+FILES = (
+    (N(''), False),
+    (N('_fake_compress'), True),
+    (N(''), True),
+    (N('_newbench'), True),
+    (N('_compress'), True),
+
+    #(N('_fake_writes'), True),
+    #(N('_fake_writes_stride'), True),
+    #(N('_fake_writes_stride1'), True),
+    #(N('_log_reduce_size'), True),
+)
+
+def datafromfile(f, persist):
+    g, l = {}, {}
+    execfile(f, g, l)
+    res, _ = split_results_by_predicate(
+        l['RESULTS'],
+        lambda x: x[0]['persist'] if persist else not x[0]['persist'])
+    return res
+
+if __name__ == '__main__':
+    def order_results_by_threads(results):
+        # res is list[(config, results)], change to
+        # list[(num_threads, results)]
+        def trfm(ent):
+            return (ent[0]['threads'], ent[1])
+        return map(trfm, results)
+
+    def extract_result_position(k, res):
+        if type(res) == list:
+            return [x[k] for x in res]
+        return res[k]
+
+    def extract_throughput(results, persist):
+        def trfm(ent):
+            return (ent[0], extract_result_position(0 if not persist else 1, ent[1]))
+        return map(trfm, results)
+
+    def extract_latency(results, persist):
+        def trfm(ent):
+            return (ent[0], extract_result_position(2 if not persist else 3, ent[1]))
+        return map(trfm, results)
+
+    def filter_name(results, name):
+        def match(ent):
+            return ent[0]['name'] == name
+        return [x for x in results if match(x)]
+
+    def XX(x):
+        return [e[0] for e in x]
+
+    def perturb(x):
+        return [np.random.normal(loc=0.0, scale=0.2) + e for e in x]
+
+    def scalaradd(x, s):
+        return [e + s for e in x]
+
+    def scale(x, s):
+        return [e / s for e in x]
+
+    def median(x): return sorted(x)[len(x)/2]
+
+    def percorify(x):
+        return [ (e[0], [ee/e[0] for ee in e[1]]) for e in x ]
+
+    def YY(x):
+        def checked(e):
+            if type(e) == list:
+                return median(e)
+            return e
+        return [checked(e[1]) for e in x]
+
+    def YYPC(x):
+        def checked(e):
+            if type(e) == list:
+                return median(e)
+            return e
+        return [checked(e[1])/float(e[0]) for e in x]
+
+    def YERR(x):
+        ypts = [e[1] for e in x]
+        ymins = np.array([min(x) for x in ypts])
+        ymaxs = np.array([max(x) for x in ypts])
+        ymid = np.array([median(x) for x in ypts])
+        yerr=np.array([ymid - ymins, ymaxs - ymid])
+        return yerr
+
+    def YERRPC(x):
+        ypts = [[ee/float(e[0]) for ee in e[1]] for e in x]
+        ymins = np.array([min(x) for x in ypts])
+        ymaxs = np.array([max(x) for x in ypts])
+        ymid = np.array([median(x) for x in ypts])
+        yerr=np.array([ymid - ymins, ymaxs - ymid])
+        return yerr
+
+    def nameit(x):
+        fname, persist = x
+        fname = fname.replace('istc3-8-1-13', '').replace('.py', '')
+        if not fname:
+            return 'base-persist' if persist else 'base'
+        return fname
+
+    fig, fig1 = plt.figure(), plt.figure()
+    ax, ax1 = fig.add_subplot(111), fig1.add_subplot(111)
+
+    from matplotlib.font_manager import FontProperties
+    fontP = FontProperties()
+    fontP.set_size('small')
+
+    off = 0.0
+    for fname, persist in FILES:
+        res = datafromfile(fname, persist)
+        res = order_results_by_threads(res)
+        throughput = extract_throughput(res, persist)
+        percorethroughput = percorify(throughput)
+        print percorethroughput
+        ax.errorbar(scalaradd(XX(throughput), off), YY(percorethroughput), yerr=YERR(percorethroughput))
+        off += 0.1
+
+    ax.legend(map(nameit, FILES), loc='lower right', prop=fontP)
+    ax.set_xlabel('threads')
+    ax.set_ylabel('throughput (txns/sec/core)')
+    ax.set_ylim([0, 32000])
+    fig.savefig('istc3-8-1-13_summary.pdf')
diff --git a/silo/benchmarks/results/make_graphs-6.py b/silo/benchmarks/results/make_graphs-6.py
new file mode 100644 (file)
index 0000000..4120856
--- /dev/null
@@ -0,0 +1,701 @@
+#!/usr/bin/env python
+
+import matplotlib
+import pylab as plt
+import numpy as np
+
+import re
+import os
+import sys
+import math
+import itertools as it
+
+# XXX: import from runner.py
+PERSIST_REAL='persist-real'
+PERSIST_TEMP='persist-temp'
+PERSIST_NONE='persist-none'
+
+NEW_ORDER_RGX = re.compile(r'--new-order-remote-item-pct (\d+)')
+def extract_raw_pct(x):
+  x = x[0]['bench_opts']
+  m = NEW_ORDER_RGX.search(x)
+  assert m
+  p = int(m.group(1))
+  assert p >= 0 and p <= 100
+  return p
+
+def extract_pct(x):
+  p = extract_raw_pct(x)
+  def pn(n, p):
+    return 1.0 - (1.0 - p)**n
+  def ex(p):
+    import math
+    return math.fsum([(1.0/11.0)*pn(float(n), p) for n in range(5, 16)])
+  return ex(p/100.0) * 100.0
+
+def mean(x):   return sum(x)/len(x)
+def median(x): return sorted(x)[len(x)/2]
+
+def extract_nthreads(x):
+  x = x[0]['threads']
+  return x
+
+def deal_with_posK_res(k):
+    def fn(x):
+        x = x[1]
+        if type(x) == list:
+            return [e[k] for e in x]
+        return x[k]
+    return fn
+
+def deal_with_posK_res_median(k):
+    def fn(x):
+        x = x[1]
+        if type(x) == list:
+            return median([e[k] for e in x])
+        return x[k]
+    return fn
+
+def extract_latency(x):
+  return deal_with_posK_res(2)(x) if x[0]['persist'] == PERSIST_NONE else \
+         deal_with_posK_res(3)(x)
+
+def deal_with_posK_res_percore(k):
+    def fn(x):
+        nthds = float(extract_nthreads(x))
+        x = x[1]
+        if type(x) == list:
+            return [e[k]/nthds for e in x]
+        return x[k]/nthds
+    return fn
+
+def longest_line(ls):
+    best, bestlen = ls[0], len(ls[0])
+    for i in xrange(1, len(ls)):
+        if len(ls[i]) > bestlen:
+            best, bestline = ls[i], len(ls[i])
+    return best
+
+def dicttokey(d):
+    return tuple(sorted(d.items(), key=lambda x: x[0]))
+
+def keytodict(k):
+    return dict(k)
+
+def merge(results):
+    def combine(ylist):
+        return list(it.chain.from_iterable(ylist))
+    d = {}
+    for r in results:
+        k = dicttokey(r[0])
+        l = d.get(k, [])
+        l.append(r[1])
+        d[k] = l
+    return [(keytodict(x), combine(ys)) for x, ys in d.iteritems()]
+
+def mkplot(results, desc, outfilename):
+    fig = plt.figure()
+    ax = plt.subplot(111)
+    double_axis = type(desc['y-axis']) == list
+    assert not double_axis or len(desc['y-axis']) == 2
+    if double_axis:
+        ax1 = ax.twinx()
+
+    lines = []
+    for line_desc in desc['lines']:
+        predfn = line_desc['extractor']
+        line_results = merge([d for d in results if predfn(d)])
+        xpts = map(desc['x-axis'], line_results)
+        if not double_axis:
+            ypts = map(desc['y-axis'], line_results)
+        else:
+            ypts = (map(desc['y-axis'][0], line_results),
+                    map(desc['y-axis'][1], line_results))
+            ypts = [(desc['y-axis'][0](x), desc['y-axis'][1](x)) for x in line_results]
+        lines.append({ 'xpts' : xpts, 'ypts' : ypts })
+    longest = longest_line([x['xpts'] for x in lines])
+    for idx in xrange(len(desc['lines'])):
+        line_desc = desc['lines'][idx]
+        if 'extend' in line_desc and line_desc['extend']:
+            assert not double_axis
+            assert len(lines[idx]['xpts']) == 1
+            lines[idx]['xpts'] = longest
+            lines[idx]['ypts'] = [lines[idx]['ypts'][0] for _ in longest]
+    # order lines
+    for i in xrange(len(lines)):
+        l = lines[i]
+        l = sorted(zip(l['xpts'], l['ypts']), key=lambda x: x[0])
+        lines[i] = { 'xpts' : [x[0] for x in l], 'ypts' : [y[1] for y in l] }
+    if not desc['show-error-bars']:
+        for l in lines:
+            if not double_axis:
+                ax.plot(l['xpts'], [median(y) for y in l['ypts']])
+            else:
+                ax.plot(l['xpts'], [median(y[0]) for y in l['ypts']])
+                ax1.plot(l['xpts'], [median(y[1]) for y in l['ypts']])
+    else:
+        for l in lines:
+            if not double_axis:
+                ymins = np.array([min(y) for y in l['ypts']])
+                ymaxs = np.array([max(y) for y in l['ypts']])
+                ymid = np.array([median(y) for y in l['ypts']])
+                yerr = np.array([ymid - ymins, ymaxs - ymid])
+                ax.errorbar(l['xpts'], ymid, yerr=yerr)
+            else:
+                ymins = np.array([min(y[0]) for y in l['ypts']])
+                ymaxs = np.array([max(y[0]) for y in l['ypts']])
+                ymid = np.array([median(y[0]) for y in l['ypts']])
+                yerr = np.array([ymid - ymins, ymaxs - ymid])
+                ax.errorbar(l['xpts'], ymid, yerr=yerr)
+
+                ymins = np.array([min(y[1]) for y in l['ypts']])
+                ymaxs = np.array([max(y[1]) for y in l['ypts']])
+                ymid = np.array([median(y[1]) for y in l['ypts']])
+                yerr = np.array([ymid - ymins, ymaxs - ymid])
+                ax1.errorbar(l['xpts'], ymid, yerr=yerr)
+
+    ax.set_xlabel(desc['x-label'])
+    ax.set_ylabel(desc['y-label'] if not double_axis else desc['y-label'][0])
+    ax.set_xlim(xmin = min(longest), xmax = max(longest))
+    ax.set_ylim(ymin = 0)
+    ax.legend([l['label'] for l in desc['lines']], loc=desc['legend'])
+    if 'y-axis-major-formatter' in desc:
+        ax.yaxis.set_major_formatter(
+            desc['y-axis-major-formatter'] if not double_axis \
+                else desc['y-axis-major-formatter'][0])
+
+    if double_axis:
+        _, axmax = ax.get_ylim()
+        ax1.set_ylabel(desc['y-label'][1])
+        ax1.set_ylim(ymin = 0, ymax = axmax)
+        if 'y-axis-major-formatter' in desc:
+            ax1.yaxis.set_major_formatter(
+                desc['y-axis-major-formatter'][1])
+
+    if 'title' in desc:
+        ax.set_title(desc['title'])
+    if 'subplots-adjust' in desc:
+        fig.subplots_adjust(**desc['subplots-adjust'])
+    fig.savefig(outfilename, format='pdf')
+
+def mkbar(results, desc, outfilename):
+    fig = plt.figure()
+    ax = plt.subplot(111)
+    bars = []
+    for bar_desc in desc['bars']:
+        predfn = bar_desc['extractor']
+        bar_results = merge([d for d in results if predfn(d)])
+        if len(bar_results) != 1:
+            print "bar_results:", bar_results
+        assert len(bar_results) == 1, 'bad predicate'
+        bars.append({ 'ypts' : desc['y-axis'](bar_results[0]) })
+    width = 0.15
+    inds = np.arange(len(bars)) * width
+    if not desc['show-error-bars']:
+        ax.bar(inds, [median(y['ypts']) for y in bars], width)
+    else:
+        def geterr(ypts):
+            ymin = min(ypts)
+            ymax = max(ypts)
+            ymid = median(ypts)
+            yerr = [ymid - ymin, ymax - ymid]
+            return yerr
+        yerrs = [[geterr(y['ypts'])[0] for y in bars],
+                 [geterr(y['ypts'])[1] for y in bars]]
+        ax.bar(inds, [median(y['ypts']) for y in bars], width, yerr=yerrs)
+    ax.set_xticks(inds + width/2.)
+    ax.set_xticklabels( [l['label'] for l in desc['bars']], rotation='vertical' )
+    ax.set_ylabel(desc['y-label'])
+    ax.set_ylim(ymin = 0)
+    if 'y-axis-major-formatter' in desc:
+        ax.yaxis.set_major_formatter(desc['y-axis-major-formatter'])
+    if 'title' in desc:
+        ax.set_title(desc['title'])
+    SI = fig.get_size_inches()
+    if 'subplots-adjust' in desc:
+        fig.subplots_adjust(**desc['subplots-adjust'])
+    fig.set_size_inches((SI[0]/2., SI[1]))
+    fig.savefig(outfilename, format='pdf')
+
+def MFormatter(x, p):
+  if x == 0:
+    return '0'
+  v = float(x)/float(10**6)
+  if math.ceil(v) == v:
+    return '%dM' % v
+  return '%.1fM' % v
+
+def KFormatter(x, p):
+  if x == 0:
+    return '0'
+  v = float(x)/float(10**3)
+  if math.ceil(v) == v:
+    return '%dK' % v
+  return '%.1fK' % v
+
+TPCC_REGULAR_MIX=[45, 43, 4, 4, 4]
+TPCC_REALISTIC_MIX=[39, 37, 4, 10, 10]
+if __name__ == '__main__':
+    matplotlib.rcParams.update({'figure.autolayout' : True})
+
+    def tpcc_fast_id_extractor(enabled):
+      if enabled:
+        return lambda x: x[0]['bench_opts'].find('--new-order-fast-id-gen') != -1
+      else:
+        return lambda x: x[0]['bench_opts'].find('--new-order-fast-id-gen') == -1
+
+    def db_extractor(db):
+      return lambda x: x[0]['db'] == db
+
+    def name_extractor(name):
+      return lambda x: x[0]['name'] == name
+
+    def persist_extractor(mode):
+      return lambda x: 'persist' in x[0] and x[0]['persist'] == mode
+
+    def binary_extractor(binary):
+      return lambda x: x[0]['binary'] == binary
+
+    def snapshots_extractor(enabled):
+      if enabled:
+        return lambda x: 'disable_snapshots' not in x[0] or not x[0]['disable_snapshots']
+      else:
+        return lambda x: 'disable_snapshots' in x[0] and x[0]['disable_snapshots']
+
+    def ro_txns_extractor(enabled):
+      if enabled:
+        return lambda x: x[0]['bench_opts'].find('--disable-read-only-snapshots') == -1
+      else:
+        return lambda x: x[0]['bench_opts'].find('--disable-read-only-snapshots') != -1
+
+    def gc_extractor(enabled):
+      if enabled:
+        return lambda x: 'disable_gc' not in x[0] or not x[0]['disable_gc']
+      else:
+        return lambda x: 'disable_gc' in x[0] and x[0]['disable_gc']
+
+    def log_compress_extractor(enabled):
+      return lambda x: 'log_compress' in x[0] and x[0]['log_compress']
+
+    def numa_extractor(enabled):
+      if enabled:
+        return lambda x: x[0]['numa_memory'] is not None
+      else:
+        return lambda x: x[0]['numa_memory'] is None
+
+    def sep_trees_extractor(enabled):
+      return lambda x: (x[0]['bench_opts'].find('--enable-separate-tree-per-partition') != -1) == enabled
+
+    def workload_mix_extractor(mix):
+      mixstr = '--workload-mix %s' % (','.join(map(str, mix)))
+      return lambda x: x[0]['bench_opts'].find(mixstr) != -1
+
+    def nthreads_extractor(nthreads):
+      return lambda x: x[0]['threads'] == nthreads
+
+    def AND(*extractors):
+      def fn(x):
+        for ex in extractors:
+          if not ex(x):
+            return False
+        return True
+      return fn
+
+    def OR(*extractors):
+      def fn(x):
+        for ex in extractors:
+          if ex(x):
+            return True
+        return False
+      return fn
+
+    configs = [
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-scale_rmw.pdf',
+        'x-axis' : extract_nthreads,
+        'y-axis' : deal_with_posK_res(0),
+        'lines' : [
+            {
+                'label' : 'Key-Value',
+                'extractor' : AND(name_extractor('scale_rmw'), db_extractor('kvdb')),
+            },
+            {
+                'label' : 'Silo',
+                'extractor' : AND(name_extractor('scale_rmw'), db_extractor('ndb-proto2')),
+            },
+            {
+                'label' : 'Silo+GlobalTID',
+                'extractor' : AND(name_extractor('scale_rmw'), db_extractor('ndb-proto1')),
+            },
+        ],
+        'x-label' : 'nthreads',
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(MFormatter),
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+        'legend' : 'upper left',
+        'title' : 'YCSB scale',
+      },
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-scale_rmw-percore.pdf',
+        'x-axis' : extract_nthreads,
+        'y-axis' : deal_with_posK_res_percore(0),
+        'lines' : [
+            {
+                'label' : 'Key-Value',
+                'extractor' : AND(name_extractor('scale_rmw'), db_extractor('kvdb')),
+            },
+            {
+                'label' : 'Silo',
+                'extractor' : AND(name_extractor('scale_rmw'), db_extractor('ndb-proto2')),
+            },
+            {
+                'label' : 'Silo+GlobalTID',
+                'extractor' : AND(name_extractor('scale_rmw'), db_extractor('ndb-proto1')),
+            },
+        ],
+        'x-label' : 'nthreads',
+        'y-label' : 'throughput/core (txns/sec/core)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(KFormatter),
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+        'legend' : 'lower left',
+        'title' : 'YCSB scale per-core',
+      },
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-scale_tpcc.pdf',
+        'x-axis' : extract_nthreads,
+        'y-axis' : deal_with_posK_res(0),
+        'lines' : [
+            {
+                'label' : 'Silo',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    persist_extractor('persist-none')),
+            },
+            {
+                'label' : 'Silo+PersistTemp',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    persist_extractor('persist-temp')),
+            },
+            {
+                'label' : 'Silo+Persist',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    persist_extractor('persist-real')),
+            },
+        ],
+        'x-label' : 'nthreads',
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(KFormatter),
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+        'legend' : 'upper left',
+        'title' : 'TPC-C scale (standard mix)',
+      },
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-scale_tpcc-percore.pdf',
+        'x-axis' : extract_nthreads,
+        'y-axis' : deal_with_posK_res_percore(0),
+        'lines' : [
+            {
+                'label' : 'Silo',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    persist_extractor('persist-none')),
+            },
+            {
+                'label' : 'Silo+PersistTemp',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    persist_extractor('persist-temp')),
+            },
+            {
+                'label' : 'Silo+Persist',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    persist_extractor('persist-real')),
+            },
+        ],
+        'x-label' : 'nthreads',
+        'y-label' : 'throughput/core (txns/sec/core)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(KFormatter),
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+        'legend' : 'lower left',
+        'title' : 'TPC-C scale per-core (standard mix)',
+      },
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-multipart_pct.pdf',
+        'x-axis' : extract_pct,
+        'y-axis' : deal_with_posK_res(0),
+        'lines' : [
+            {
+                'label' : 'Partition-Store',
+                'extractor' : AND(
+                    name_extractor('multipart:pct'),
+                    db_extractor('kvdb-st')),
+            },
+            {
+                'label' : 'Maflingo',
+                'extractor' : AND(
+                    name_extractor('multipart:pct'),
+                    db_extractor('ndb-proto2'),
+                    snapshots_extractor(True)),
+            },
+            {
+                'label' : 'Maflingo+NoSS',
+                'extractor' : AND(
+                    name_extractor('multipart:pct'),
+                    db_extractor('ndb-proto2'),
+                    snapshots_extractor(False),
+                    sep_trees_extractor(False)),
+            },
+            {
+                'label' : 'Partition-Maflingo+NoSS',
+                'extractor' : AND(
+                    name_extractor('multipart:pct'),
+                    db_extractor('ndb-proto2'),
+                    snapshots_extractor(False),
+                    sep_trees_extractor(True)),
+            },
+        ],
+        'x-label' : '% cross-partition',
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(MFormatter),
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+        'legend' : 'upper right',
+        'title'  : 'TPC-C new order multi-partition',
+      },
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-multipart_skew.pdf',
+        'x-axis' : extract_nthreads,
+        'y-axis' : deal_with_posK_res(0),
+        'lines' : [
+            {
+                'label' : 'Partition-Store',
+                'extractor' : AND(name_extractor('multipart:skew'), db_extractor('kvdb-st')),
+                'extend' : True,
+            },
+            {
+                'label' : 'Silo',
+                'extractor' : AND(
+                    name_extractor('multipart:skew'),
+                    db_extractor('ndb-proto2'),
+                    tpcc_fast_id_extractor(False)),
+            },
+            {
+                'label' : 'Silo+FastIds',
+                'extractor' : AND(
+                    name_extractor('multipart:skew'),
+                    db_extractor('ndb-proto2'),
+                    tpcc_fast_id_extractor(True)),
+            },
+        ],
+        'x-label' : 'nthreads',
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(KFormatter),
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+        'legend' : 'upper left',
+        'title'  : 'TPC-C new order skew',
+      },
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-factor-analysis.pdf',
+        'y-axis' : deal_with_posK_res(0),
+        'bars' : [
+            {
+                'label' : 'Baseline',
+                'extractor' : AND(
+                    name_extractor('factoranalysis'),
+                    db_extractor('ndb-proto2'),
+                    binary_extractor('../out-factor-gc-nowriteinplace/benchmarks/dbtest'),
+                    snapshots_extractor(True),
+                    numa_extractor(False)),
+            },
+            {
+                'label' : '+NumaAllocator',
+                'extractor' : AND(
+                    name_extractor('factoranalysis'),
+                    db_extractor('ndb-proto2'),
+                    binary_extractor('../out-factor-gc-nowriteinplace/benchmarks/dbtest'),
+                    snapshots_extractor(True),
+                    numa_extractor(True)),
+            },
+            {
+                'label' : '+Overwrites',
+                'extractor' : AND(
+                    name_extractor('factoranalysis'),
+                    db_extractor('ndb-proto2'),
+                    binary_extractor('../out-factor-gc/benchmarks/dbtest'),
+                    snapshots_extractor(True),
+                    numa_extractor(True)),
+            },
+            {
+                'label' : '-Snapshots',
+                'extractor' : AND(
+                    name_extractor('factoranalysis'),
+                    db_extractor('ndb-proto2'),
+                    binary_extractor('../out-factor-gc/benchmarks/dbtest'),
+                    snapshots_extractor(False),
+                    gc_extractor(True),
+                    numa_extractor(True)),
+            },
+            {
+                'label' : '-GC',
+                'extractor' : AND(
+                    name_extractor('factoranalysis'),
+                    db_extractor('ndb-proto2'),
+                    binary_extractor('../out-factor-gc/benchmarks/dbtest'),
+                    snapshots_extractor(False),
+                    gc_extractor(False),
+                    numa_extractor(True)),
+            },
+        ],
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(KFormatter),
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+        'subplots-adjust' : {'bottom' : 0.25},
+      },
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-persist-factor-analysis.pdf',
+        'y-axis' : deal_with_posK_res(0),
+        'bars' : [
+            {
+                'label' : 'NoPersist',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    nthreads_extractor(28),
+                    persist_extractor('persist-none'),
+                    db_extractor('ndb-proto2')),
+            },
+            {
+                'label' : 'ConstRecs',
+                'extractor' : AND(
+                    name_extractor('persistfactoranalysis'),
+                    binary_extractor('../out-factor-fake-compression/benchmarks/dbtest'),
+                    persist_extractor('persist-real'),
+                    numa_extractor(True)),
+            },
+            {
+                'label' : 'Regular',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    nthreads_extractor(28),
+                    persist_extractor('persist-real'),
+                    db_extractor('ndb-proto2')),
+            },
+            {
+                'label' : 'Compress',
+                'extractor' : AND(
+                    name_extractor('persistfactoranalysis'),
+                    persist_extractor('persist-real'),
+                    log_compress_extractor(True),
+                    numa_extractor(True)),
+            },
+        ],
+        'y-label' : 'throughput (txns/sec)',
+        'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(KFormatter),
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+        'subplots-adjust' : {'bottom' : 0.2},
+      },
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-readonly.pdf',
+        'x-axis' : extract_raw_pct,
+        'y-axis' : [deal_with_posK_res(0), deal_with_posK_res(4)],
+        'lines' : [
+            {
+                'label' : '+Snapshots',
+                'extractor' : AND(
+                    name_extractor('readonly'),
+                    snapshots_extractor(True)),
+            },
+            {
+                'label' : '-Snapshots',
+                'extractor' : AND(
+                    name_extractor('readonly'),
+                    snapshots_extractor(False)),
+            },
+        ],
+        'x-label' : '% remote warehouse',
+        'y-label' : ['throughput (txns/sec)', 'aborts/sec'],
+        'y-axis-major-formatter' : [
+            matplotlib.ticker.FuncFormatter(KFormatter),
+            matplotlib.ticker.FuncFormatter(KFormatter)
+        ],
+        'legend' : 'right',
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+      },
+      {
+        'file'    : 'istc3-9-8-13.py',
+        'outfile' : 'istc3-9-8-13-scale_tpcc-latency.pdf',
+        'x-axis' : deal_with_posK_res_median(0),
+        'y-axis' : extract_latency,
+        'lines' : [
+            {
+                'label' : 'Silo',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    persist_extractor('persist-none')),
+            },
+            {
+                'label' : 'Silo+PersistTemp',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    persist_extractor('persist-temp')),
+            },
+            {
+                'label' : 'Silo+Persist',
+                'extractor' : AND(
+                    name_extractor('scale_tpcc'),
+                    persist_extractor('persist-real')),
+            },
+        ],
+        'x-label' : 'throughput (txns/sec)',
+        'y-label' : 'latency (ms)',
+        #'y-axis-major-formatter' : matplotlib.ticker.FuncFormatter(KFormatter),
+        'x-axis-set-major-locator' : False,
+        'show-error-bars' : True,
+        'legend' : 'upper left',
+        'title' : 'TPC-C scale (standard mix)',
+      }
+    ]
+
+    def extract_from_files(f):
+        if type(f) == list:
+            return list(it.chain.from_iterable([extract_from_files(ff) for ff in f]))
+        g, l = {}, {}
+        execfile(f, g, l)
+        return l['RESULTS']
+
+    FINAL_OUTPUT_FILENAME='istc3-cameraready.pdf'
+    from PyPDF2 import PdfFileWriter, PdfFileReader
+    output = PdfFileWriter()
+    for config in configs:
+    #for config in [configs[-1]]:
+      res = extract_from_files(config['file'])
+      if 'lines' in config:
+        mkplot(res, config, config['outfile'])
+      elif 'bars' in config:
+        mkbar(res, config, config['outfile'])
+      else:
+        assert False, "bad config"
+      inp = PdfFileReader(open(config['outfile'], 'rb'))
+      output.addPage(inp.getPage(0))
+      print >>sys.stderr, '[INFO] finished', config['outfile']
+
+    output.write(file(FINAL_OUTPUT_FILENAME, 'wb'))
diff --git a/silo/benchmarks/results/make_graphs.py b/silo/benchmarks/results/make_graphs.py
new file mode 100755 (executable)
index 0000000..7d81164
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+import matplotlib
+import pylab as plt
+import numpy as np
+
+import os
+import sys
+
+if __name__ == '__main__':
+  files = sys.argv[1:]
+  for f in files:
+    execfile(f)
+    for (db, res) in zip(DBS[:-1], RESULTS):
+      #plt.plot(THREADS, np.log(np.array(res)))
+      #plt.plot(THREADS, res)
+      #plt.plot(THREADS, np.array(res)/np.array(THREADS)) # per-core
+      plt.plot(THREADS, np.log10(np.array(res)/np.array(THREADS))) # per-core
+    plt.xlabel('num threads')
+    #plt.ylabel('ops/sec')
+    #plt.ylabel('ops/sec/core')
+    plt.ylabel('$log_{10}$ ops/sec/thread')
+    plt.legend(DBS[:-1], loc='right')
+    plt.title('YCSB workload 95/4/1 read/rmw/write 10M keys')
+    plt.savefig('.'.join(os.path.basename(f).split('.')[:-1] + ['pdf']))
+    plt.close()
diff --git a/silo/benchmarks/results/tom-1-22-13.py b/silo/benchmarks/results/tom-1-22-13.py
new file mode 100644 (file)
index 0000000..ce602be
--- /dev/null
@@ -0,0 +1,4 @@
+#DBS = ('mysql', 'bdb', 'ndb-proto1', 'ndb-proto2')
+DBS = ('mysql', 'berkeley-db', 'nu-db', 'ndb-proto2')
+THREADS = (1, 2, 4, 8, 16, 24, 32, 40, 48)
+RESULTS = [[24379.9, 39409.1, 69521.0, 111504.0, 93520.6, 74206.8, 59526.8, 29637.5, 24295.9], [133844.0, 78811.2, 82557.7, 76093.0, 77323.0, 78700.7, 76911.0, 41490.7, 38417.1], [342565.0, 793420.0, 1467000.0, 2850810.0, 4767900.0, 5926280.0, 6401950.0, 7045150.0, 6379670.0], [535023.0, 804012.0, 1551490.0, 2866770.0, 4857820.0, 7156630.0, 8767890.0, 9970540.0, 9252390.0]]
diff --git a/silo/benchmarks/results/tom-2-13-13.py b/silo/benchmarks/results/tom-2-13-13.py
new file mode 100644 (file)
index 0000000..11222b0
--- /dev/null
@@ -0,0 +1 @@
+RESULTS = [({'scale_factor': 48, 'threads': 48, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (225679.0, 3.66666)), ({'scale_factor': 48, 'threads': 48, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (288234.0, 3.36666)), ({'scale_factor': 42, 'threads': 42, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (221730.0, 3.56666)), ({'scale_factor': 42, 'threads': 42, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (247879.0, 2.36666)), ({'scale_factor': 36, 'threads': 36, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (224701.0, 3.06666)), ({'scale_factor': 36, 'threads': 36, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (245265.0, 2.36666)), ({'scale_factor': 30, 'threads': 30, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (208188.0, 3.36666)), ({'scale_factor': 30, 'threads': 30, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (217769.0, 2.69999)), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (170535.0, 2.49999)), ({'scale_factor': 24, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (175420.0, 1.69999)), ({'scale_factor': 18, 'threads': 18, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (133891.0, 2.29999)), ({'scale_factor': 18, 'threads': 18, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (138354.0, 1.53333)), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (94429.6, 1.53333)), ({'scale_factor': 12, 'threads': 12, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (96529.3, 1.13333)), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (66287.5, 1.1)), ({'scale_factor': 8, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (66264.2, 0.966663)), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (34313.2, 0.466665)), ({'scale_factor': 4, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (34307.9, 0.199999)), ({'scale_factor': 2, 'threads': 2, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (17625.4, 0.133333)), ({'scale_factor': 2, 'threads': 2, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (17263.5, 0.199999)), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, (9129.21, 0.0)), ({'scale_factor': 1, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, (9053.26, 0.0))]
diff --git a/silo/benchmarks/results/tom-2-6-13.py b/silo/benchmarks/results/tom-2-6-13.py
new file mode 100644 (file)
index 0000000..2543927
--- /dev/null
@@ -0,0 +1,6 @@
+DBS = ('ndb-proto1', 'ndb-proto2')
+THREADS = (1, 2, 4, 8, 16, 24, 32, 40, 48)
+TXN_FLAGS = (1,)
+SCALE_FACTORS = (10,)
+BENCHMARKS = ('tpcc',)
+RESULTS = [({'scale_factor': 10, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, 1484.09), ({'scale_factor': 10, 'threads': 2, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, 2890.06), ({'scale_factor': 10, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, 5741.49), ({'scale_factor': 10, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, 11037.6), ({'scale_factor': 10, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, 18200.4), ({'scale_factor': 10, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, 23739.9), ({'scale_factor': 10, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, 28485.0), ({'scale_factor': 10, 'threads': 40, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, 32868.2), ({'scale_factor': 10, 'threads': 48, 'txn_flags': 1, 'db': 'ndb-proto1', 'bench': 'tpcc'}, 36221.4), ({'scale_factor': 10, 'threads': 1, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, 1479.2), ({'scale_factor': 10, 'threads': 2, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, 2928.76), ({'scale_factor': 10, 'threads': 4, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, 5767.42), ({'scale_factor': 10, 'threads': 8, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, 11211.6), ({'scale_factor': 10, 'threads': 16, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, 19651.7), ({'scale_factor': 10, 'threads': 24, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, 26213.1), ({'scale_factor': 10, 'threads': 32, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, 31440.7), ({'scale_factor': 10, 'threads': 40, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, 35715.4), ({'scale_factor': 10, 'threads': 48, 'txn_flags': 1, 'db': 'ndb-proto2', 'bench': 'tpcc'}, 40504.7)]
diff --git a/silo/benchmarks/runner.py b/silo/benchmarks/runner.py
new file mode 100644 (file)
index 0000000..5e33d77
--- /dev/null
@@ -0,0 +1,759 @@
+#!/usr/bin/env python
+
+import itertools as it
+import platform
+import math
+import subprocess
+import sys
+import multiprocessing as mp
+import os
+import re
+
+DRYRUN = True
+USE_MASSTREE = True
+
+NTRIALS = 1 if DRYRUN else 3
+
+PERSIST_REAL='persist-real'
+PERSIST_TEMP='persist-temp'
+PERSIST_NONE='persist-none'
+
+MACHINE_CONFIG = {
+  'modis2' : {
+      'logfiles' : (
+          ('data.log', 1.),
+          ('/data/scidb/001/2/stephentu/data.log', 1.),
+          ('/data/scidb/001/3/stephentu/data.log', 1.),
+      ),
+      'tempprefix' : '/tmp',
+      'disable_madv_willneed' : False,
+  },
+  'istc3' : {
+      'logfiles' : (
+          ('data.log', 3./24.),
+          ('/f0/stephentu/data.log', 7./24.),
+          ('/f1/stephentu/data.log', 7./24.),
+          ('/f2/stephentu/data.log', 7./24.),
+      ),
+      'tempprefix' : '/run/shm',
+      'disable_madv_willneed' : True,
+  },
+  'istc4' : {
+      'logfiles' : (
+          ('data.log', 1.),
+      ),
+      'tempprefix' : '/run/shm',
+      'disable_madv_willneed' : False,
+  },
+}
+
+NCPUS = mp.cpu_count()
+
+TPCC_STANDARD_MIX='45,43,4,4,4'
+TPCC_REALISTIC_MIX='39,37,4,10,10'
+
+KNOB_ENABLE_YCSB_SCALE=True
+KNOB_ENABLE_TPCC_SCALE=True
+KNOB_ENABLE_TPCC_MULTIPART=True
+KNOB_ENABLE_TPCC_MULTIPART_SKEW=True
+KNOB_ENABLE_TPCC_FACTOR_ANALYSIS=True
+KNOB_ENABLE_TPCC_PERSIST_FACTOR_ANALYSIS=True
+KNOB_ENABLE_TPCC_RO_SNAPSHOTS=True
+
+## debugging runs
+KNOB_ENABLE_TPCC_SCALE_ALLPERSIST=False
+KNOB_ENABLE_TPCC_SCALE_ALLPERSIST_COMPRESS=False
+KNOB_ENABLE_TPCC_SCALE_ALLPERSIST_NOFSYNC=False
+KNOB_ENABLE_TPCC_SCALE_FAKEWRITES=False
+KNOB_ENABLE_TPCC_SCALE_GC=False
+KNOB_ENABLE_TPCC_FACTOR_ANALYSIS_1=False
+
+def binary_path(tpe):
+  prog_suffix= '.masstree' if USE_MASSTREE else '.silotree'
+  return '../%s%s/benchmarks/dbtest' % (tpe, prog_suffix)
+
+grids = []
+
+def get_scale_threads(stride):
+  thds = range(0, NCPUS + 1, stride)
+  thds[0] = 1
+  return thds
+
+### helpers for log allocation
+def normalize(x):
+  denom = math.fsum(x)
+  return [e / denom for e in x]
+
+def scale(x, a):
+  return [e * a for e in x]
+
+# a - b
+def sub(a, b):
+  assert len(a) == len(b)
+  return [x - y for x, y in zip(a, b)]
+
+def twonorm(x):
+  return math.sqrt(math.fsum([e * e for e in x]))
+
+def onenorm(x):
+  return math.fsum([abs(e) for e in x])
+
+def argcmp(x, comp, predicate):
+  idx = None
+  val = None
+  for i in xrange(len(x)):
+    if not predicate(x[i]):
+      continue
+    if idx is None or comp(x[i], val):
+      idx = i
+      val = x[i]
+  if idx is None:
+    # couldn't find it
+    raise Exception("no argmin satisfiying predicate")
+  return idx
+
+def argmin(x, predicate=lambda x: True):
+  return argcmp(x, lambda a, b: a < b, predicate)
+
+def argmax(x, predicate=lambda x: True):
+  return argcmp(x, lambda a, b: a > b, predicate)
+
+def allocate(nworkers, weights):
+  def score(allocation):
+    #print "score(): allocation=", allocation, "weighted=", normalize(allocation), \
+    #    "score=",onenorm(sub(normalize(allocation), weights))
+    return onenorm(sub(normalize(allocation), weights))
+
+  # assumes weights are normalized
+  approx = map(int, map(math.ceil, scale(weights, nworkers)))
+  diff = sum(approx) - nworkers
+  if diff > 0:
+    #print "OVER"
+    #print approx
+    #print normalize(approx)
+    while diff > 0:
+      best, bestValue = None, None
+      for idx in xrange(len(approx)):
+        if not approx[idx]:
+          continue
+        cpy = approx[:]
+        cpy[idx] -= 1
+        s = score(cpy)
+        if bestValue is None or s < bestValue:
+          best, bestValue = cpy, s
+      assert best is not None
+      approx = best
+      diff -= 1
+
+  elif diff < 0:
+    #print "UNDER"
+    #print approx
+    #print normalize(approx)
+    while diff < 0:
+      best, bestValue = None, None
+      for idx in xrange(len(approx)):
+        cpy = approx[:]
+        cpy[idx] += 1
+        s = score(cpy)
+        if bestValue is None or s < bestValue:
+          best, bestValue = cpy, s
+      assert best is not None
+      approx = best
+      diff += 1
+
+  #print "choice      =", approx
+  #print "weights     =", weights
+  #print "allocweights=", normalize(approx)
+
+  acc = 0
+  ret = []
+  for x in approx:
+    ret.append(range(acc, acc + x))
+    acc += x
+  return ret
+
+if KNOB_ENABLE_YCSB_SCALE:
+  def mk_ycsb_entries(nthds):
+    return [
+      {
+        'name' : 'scale_rmw',
+        'dbs' : ['kvdb', 'ndb-proto1', 'ndb-proto2'],
+        'threads' : [nthds],
+        'scale_factors' : [160000],
+        'benchmarks' : ['ycsb'],
+        'bench_opts' : ['--workload-mix 80,0,20,0'],
+        'par_load' : [True],
+        'retry' : [False],
+        'persist' : [PERSIST_NONE],
+        'numa_memory' : ['%dG' % (40 + 2 * nthds)],
+      },
+    ]
+  THREADS = get_scale_threads(4)
+  for nthds in THREADS:
+    grids += mk_ycsb_entries(nthds)
+
+# exp 2:
+if KNOB_ENABLE_TPCC_SCALE:
+  def mk_grid(name, bench, nthds):
+    return {
+      'name' : name,
+      'dbs' : ['ndb-proto2'],
+      'threads' : [nthds],
+      'scale_factors' : [nthds],
+      'benchmarks' : [bench],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_REAL, PERSIST_TEMP, PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * nthds)],
+    }
+  THREADS = get_scale_threads(4)
+  grids += [mk_grid('scale_tpcc', 'tpcc', t) for t in THREADS]
+
+# exp 3:
+#   x-axis varies the % multi-partition for new order. hold scale_factor constant @ 28,
+#   nthreads also constant at 28
+if KNOB_ENABLE_TPCC_MULTIPART:
+  D_RANGE = range(0, 11)
+  grids += [
+    {
+      'name' : 'multipart:pct',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' :
+          ['--workload-mix 100,0,0,0,0 --new-order-remote-item-pct %d' % d for d in D_RANGE],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 28)],
+    },
+    {
+      'binary' : [binary_path('out-factor-gc')],
+      'name' : 'multipart:pct',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' :
+          ['--workload-mix 100,0,0,0,0 --new-order-remote-item-pct %d' % d for d in D_RANGE],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'disable_snapshots' : [True],
+      'numa_memory' : ['%dG' % (4 * 28)],
+    },
+    {
+      'binary' : [binary_path('out-factor-gc')],
+      'name' : 'multipart:pct',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' :
+          ['--enable-separate-tree-per-partition --workload-mix 100,0,0,0,0 --new-order-remote-item-pct %d' % d for d in D_RANGE],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'disable_snapshots' : [True],
+      'numa_memory' : ['%dG' % (4 * 28)],
+    },
+    {
+      'name' : 'multipart:pct',
+      'dbs' : ['kvdb-st'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' :
+        ['--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks --new-order-remote-item-pct %d' % d for d in D_RANGE],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 28)],
+    },
+  ]
+
+if KNOB_ENABLE_TPCC_MULTIPART_SKEW:
+  def mk_grids(nthds):
+    return [
+      {
+        'name' : 'multipart:skew',
+        'dbs' : ['ndb-proto2'],
+        'threads' : [nthds],
+        'scale_factors': [4],
+        'benchmarks' : ['tpcc'],
+        'bench_opts' : [
+          '--workload-mix 100,0,0,0,0',
+        ],
+        'par_load' : [False],
+        'retry' : [True],
+        'backoff' : [True],
+        'persist' : [PERSIST_NONE],
+        'numa_memory' : ['%dG' % (4 * nthds)],
+      },
+      {
+        'name' : 'multipart:skew',
+        'dbs' : ['ndb-proto2'],
+        'threads' : [nthds],
+        'scale_factors': [4],
+        'benchmarks' : ['tpcc'],
+        'bench_opts' : [
+          '--workload-mix 100,0,0,0,0 --new-order-fast-id-gen'
+        ],
+        'par_load' : [False],
+        'retry' : [True],
+        'persist' : [PERSIST_NONE],
+        'numa_memory' : ['%dG' % (4 * nthds)],
+      },
+    ]
+  grids += [
+    {
+      'name' : 'multipart:skew',
+      'dbs' : ['kvdb-st'],
+      'threads' : [1],
+      'scale_factors': [4],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' :
+        ['--workload-mix 100,0,0,0,0 --enable-separate-tree-per-partition --enable-partition-locks'],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 4)],
+    },
+  ]
+  thds = [1,2,4,6,8,10,12,16,20,24,28,32]
+  grids += list(it.chain.from_iterable([mk_grids(t) for t in thds]))
+
+if KNOB_ENABLE_TPCC_FACTOR_ANALYSIS:
+  # order is:
+  # baseline (jemalloc, no-overwrites, gc, snapshots)
+  # +allocator
+  # +insert
+  # -snapshots
+  # -gc
+  grids += [
+    {
+      'binary' : [binary_path('out-factor-gc-nowriteinplace')],
+      'name' : 'factoranalysis',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : [None, '%dG' % (4 * 28)],
+    },
+    {
+      'binary' : [binary_path('out-factor-gc')],
+      'name' : 'factoranalysis',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 28)],
+      'disable_snapshots' : [False],
+    },
+    {
+      'binary' : [binary_path('out-factor-gc')],
+      'name' : 'factoranalysis',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' : ['--disable-read-only-snapshots'],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 28)],
+      'disable_snapshots' : [True],
+    },
+    {
+      'binary' : [binary_path('out-factor-gc')],
+      'name' : 'factoranalysis',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' : ['--disable-read-only-snapshots'],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 28)],
+      'disable_snapshots' : [True],
+      'disable_gc' : [True],
+    },
+  ]
+
+if KNOB_ENABLE_TPCC_FACTOR_ANALYSIS_1:
+  # order is:
+  # baseline (jemalloc, no-overwrites, gc, no-snapshots)
+  # +allocator
+  # +insert
+  # +snapshots
+  # -gc
+  grids += [
+    {
+      'binary' : [binary_path('out-factor-gc-nowriteinplace')],
+      'name' : 'factoranalysis',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' : ['--workload-mix %s --disable-read-only-snapshots' % TPCC_REALISTIC_MIX],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : [None, '%dG' % (4 * 28)],
+      'disable_snapshots': [True],
+    },
+    {
+      'binary' : [binary_path('out-factor-gc')],
+      'name' : 'factoranalysis',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' : ['--workload-mix %s --disable-read-only-snapshots' % TPCC_REALISTIC_MIX],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 28)],
+      'disable_snapshots' : [True],
+    },
+    {
+      'binary' : [binary_path('out-factor-gc')],
+      'name' : 'factoranalysis',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' : ['--workload-mix %s' % TPCC_REALISTIC_MIX],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 28)],
+      'disable_gc' : [False, True],
+    },
+  ]
+
+if KNOB_ENABLE_TPCC_PERSIST_FACTOR_ANALYSIS:
+  # write zero length log records (perfect/fake compression)
+  # lz4-compress buffers
+  grids += [
+    {
+      'binary' : [binary_path('out-factor-fake-compression')],
+      'name' : 'persistfactoranalysis',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_REAL],
+      'numa_memory' : ['%dG' % (4 * 28)],
+    },
+    {
+      'binary' : [binary_path('out-perf')],
+      'name' : 'persistfactoranalysis',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [28],
+      'scale_factors': [28],
+      'benchmarks' : ['tpcc'],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_REAL],
+      'numa_memory' : ['%dG' % (4 * 28)],
+      'log_compress' : [True],
+    },
+  ]
+
+# exp 5:
+#  * 50% new order, 50% stock level
+#  * scale factor 8, n-threads 16
+#  * x-axis is --new-order-remote-item-pct from [0, 20, 40, 60, 80, 100]
+if KNOB_ENABLE_TPCC_RO_SNAPSHOTS:
+  RO_DRANGE = [0, 20, 40, 60, 80, 100]
+  grids += [
+    {
+      'name' : 'readonly',
+      'dbs' : ['ndb-proto2'],
+      'threads' : [16],
+      'scale_factors': [8],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' : ['--workload-mix 50,0,0,0,50 --new-order-remote-item-pct %d' % d for d in RO_DRANGE],
+      'par_load' : [False],
+      'retry' : [True],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 16)],
+      'disable_snapshots' : [False],
+    },
+    {
+      'name' : 'readonly',
+      'binary' : [binary_path('out-factor-gc')],
+      'dbs' : ['ndb-proto2'],
+      'threads' : [16],
+      'scale_factors': [8],
+      'benchmarks' : ['tpcc'],
+      'bench_opts' : ['--disable-read-only-snapshots --workload-mix 50,0,0,0,50 --new-order-remote-item-pct %d' % d for d in RO_DRANGE],
+      'par_load' : [False],
+      'retry' : [True],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * 16)],
+      'disable_snapshots' : [True],
+    },
+  ]
+
+if KNOB_ENABLE_TPCC_SCALE_ALLPERSIST:
+  def mk_grid(name, bench, nthds):
+    return {
+      'name' : name,
+      'dbs' : ['ndb-proto2'],
+      'threads' : [nthds],
+      'scale_factors' : [nthds],
+      'benchmarks' : [bench],
+      'bench_opts' : [''],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_REAL],
+      'numa_memory' : ['%dG' % (4 * nthds)],
+    }
+  THREADS = get_scale_threads(4)
+  grids += [mk_grid('scale_tpcc', 'tpcc', t) for t in THREADS]
+
+if KNOB_ENABLE_TPCC_SCALE_ALLPERSIST_COMPRESS:
+  def mk_grid(name, bench, nthds):
+    return {
+      'name' : name,
+      'dbs' : ['ndb-proto2'],
+      'threads' : [nthds],
+      'scale_factors' : [nthds],
+      'benchmarks' : [bench],
+      'bench_opts' : [''],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_REAL],
+      'numa_memory' : ['%dG' % (4 * nthds)],
+      'log_compress' : [True],
+    }
+  THREADS = get_scale_threads(4)
+  grids += [mk_grid('scale_tpcc', 'tpcc', t) for t in THREADS]
+
+if KNOB_ENABLE_TPCC_SCALE_ALLPERSIST_NOFSYNC:
+  def mk_grid(name, bench, nthds):
+    return {
+      'name' : name,
+      'dbs' : ['ndb-proto2'],
+      'threads' : [nthds],
+      'scale_factors' : [nthds],
+      'benchmarks' : [bench],
+      'bench_opts' : [''],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_REAL],
+      'numa_memory' : ['%dG' % (4 * nthds)],
+      'log_nofsync' : [True],
+    }
+  THREADS = get_scale_threads(4)
+  grids += [mk_grid('scale_tpcc', 'tpcc', t) for t in THREADS]
+
+if KNOB_ENABLE_TPCC_SCALE_FAKEWRITES:
+  def mk_grid(name, bench, nthds):
+    return {
+      'name' : name,
+      'dbs' : ['ndb-proto2'],
+      'threads' : [nthds],
+      'scale_factors' : [nthds],
+      'benchmarks' : [bench],
+      'bench_opts' : [''],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_REAL],
+      'numa_memory' : ['%dG' % (4 * nthds)],
+      'log_fake_writes' : [True],
+    }
+  THREADS = get_scale_threads(4)
+  grids += [mk_grid('scale_tpcc', 'tpcc', t) for t in THREADS]
+
+if KNOB_ENABLE_TPCC_SCALE_GC:
+  def mk_grid(name, bench, nthds):
+    return {
+      'name' : name,
+      'dbs' : ['ndb-proto2'],
+      'threads' : [nthds],
+      'scale_factors' : [nthds],
+      'benchmarks' : [bench],
+      'bench_opts' : [''],
+      'par_load' : [False],
+      'retry' : [False],
+      'persist' : [PERSIST_NONE],
+      'numa_memory' : ['%dG' % (4 * nthds)],
+      'disable_gc' : [False, True],
+    }
+  THREADS = get_scale_threads(4)
+  grids += [mk_grid('scale_tpcc', 'tpcc', t) for t in THREADS]
+
+def check_binary_executable(binary):
+  return os.path.isfile(binary) and os.access(binary, os.X_OK)
+
+def run_configuration(
+    binary, disable_madv_willneed,
+    basedir, dbtype, bench, scale_factor, nthreads, bench_opts,
+    par_load, retry_aborted_txn, backoff_aborted_txn, numa_memory, logfiles,
+    assignments, log_fake_writes, log_nofsync, log_compress,
+    disable_gc, disable_snapshots, ntries=5):
+  # Note: assignments is a list of list of ints
+  assert len(logfiles) == len(assignments)
+  assert not log_fake_writes or len(logfiles)
+  assert not log_nofsync or len(logfiles)
+  assert not log_compress or len(logfiles)
+  args = [
+      binary,
+      '--bench', bench,
+      '--basedir', basedir,
+      '--db-type', dbtype,
+      '--num-threads', str(nthreads),
+      '--scale-factor', str(scale_factor),
+      '--txn-flags', '1',
+      '--runtime', '60',
+  ] + ([] if not bench_opts else ['--bench-opts', bench_opts]) \
+    + ([] if not par_load else ['--parallel-loading']) \
+    + ([] if not retry_aborted_txn else ['--retry-aborted-transactions']) \
+    + ([] if not backoff_aborted_txn else ['--backoff-aborted-transactions']) \
+    + ([] if not numa_memory else ['--numa-memory', numa_memory]) \
+    + ([] if not logfiles else list(it.chain.from_iterable([['--logfile', f] for f in logfiles]))) \
+    + ([] if not assignments else list(it.chain.from_iterable([['--assignment', ','.join(map(str, x))] for x in assignments]))) \
+    + ([] if not log_fake_writes else ['--log-fake-writes']) \
+    + ([] if not log_nofsync else ['--log-nofsync']) \
+    + ([] if not log_compress else ['--log-compress']) \
+    + ([] if not disable_gc else ['--disable-gc']) \
+    + ([] if not disable_snapshots else ['--disable-snapshots'])
+  print >>sys.stderr, '[INFO] running command:'
+  print >>sys.stderr, ('DISABLE_MADV_WILLNEED=1' if disable_madv_willneed else ''), ' '.join([x.replace(' ', r'\ ') for x in args])
+  if not DRYRUN:
+    with open('stderr.log', 'w') as err:
+      env = dict(os.environ)
+      if disable_madv_willneed:
+        env['DISABLE_MADV_WILLNEED'] = '1'
+      p = subprocess.Popen(args, stdin=open('/dev/null', 'r'), stdout=subprocess.PIPE, stderr=err, env=env)
+      print >>sys.stderr, 'pid=', p.pid
+      r = p.stdout.read()
+      retcode = p.wait()
+      toks = r.strip().split(' ')
+  else:
+    assert check_binary_executable(binary)
+    toks = [0,0,0,0,0]
+  if len(toks) != 5:
+    print 'Failure: retcode=', retcode, ', stdout=', r
+    import shutil
+    shutil.copyfile('stderr.log', 'stderr.%d.log' % p.pid)
+    if ntries:
+      return run_configuration(
+          binary, disable_madv_willneed,
+          basedir, dbtype, bench, scale_factor, nthreads, bench_opts,
+          par_load, retry_aborted_txn, backoff_aborted_txn, numa_memory, logfiles,
+          assignments, log_fake_writes, log_nofsync, log_compress,
+          disable_gc, disable_snapshots, ntries - 1)
+    else:
+      print "Out of tries!"
+      assert False
+  return tuple(map(float, toks))
+
+if __name__ == '__main__':
+  (_, basedir, outfile) = sys.argv
+
+  DEFAULT_BINARY=binary_path('out-perf')
+  # list all the binaries needed
+  binaries = set(it.chain.from_iterable([grid.get('binary', [DEFAULT_BINARY]) for grid in grids]))
+  failed = []
+  for binary in binaries:
+    if not check_binary_executable(binary):
+      print >>sys.stderr, '[ERROR] cannot find binary %s' % binary
+      failed.append(binary)
+  if failed:
+    r = re.compile(r'out-(.*)\.(masstree|silotree)')
+    print >>sys.stderr, \
+        '[INFO] Try running the following commands in the root source directory:'
+    for binary in failed:
+      folder = binary.split(os.sep)[1]
+      m = r.match(folder)
+      if not m:
+        print >>sys.stderr, '[ERROR] bad binary name %s' % binary
+      else:
+        print >>sys.stderr, 'MASSTREE=%d MODE=%s make -j dbtest' % (1 if m.group(2) == 'masstree' else 0, m.group(1))
+    sys.exit(1)
+
+  # iterate over all configs
+  results = []
+  for grid in grids:
+    for (binary, db, bench, scale_factor, threads, bench_opts,
+         par_load, retry, backoff, numa_memory, persist,
+         log_fake_writes, log_nofsync, log_compress,
+         disable_gc, disable_snapshots) in it.product(
+        grid.get('binary', [DEFAULT_BINARY]),
+        grid['dbs'], grid['benchmarks'], grid['scale_factors'],
+        grid['threads'], grid.get('bench_opts', ['']), grid['par_load'],
+        grid['retry'], grid.get('backoff', [False]),
+        grid['numa_memory'], grid['persist'],
+        grid.get('log_fake_writes', [False]),
+        grid.get('log_nofsync', [False]),
+        grid.get('log_compress', [False]),
+        grid.get('disable_gc', [False]),
+        grid.get('disable_snapshots', [False])):
+      node = platform.node()
+      disable_madv_willneed = MACHINE_CONFIG[node]['disable_madv_willneed']
+      config = {
+        'binary'                : binary,
+        'disable_madv_willneed' : disable_madv_willneed,
+        'name'                  : grid['name'],
+        'db'                    : db,
+        'bench'                 : bench,
+        'scale_factor'          : scale_factor,
+        'threads'               : threads,
+        'bench_opts'            : bench_opts,
+        'par_load'              : par_load,
+        'retry'                 : retry,
+        'backoff'               : backoff,
+        'persist'               : persist,
+        'numa_memory'           : numa_memory,
+        'log_fake_writes'       : log_fake_writes,
+        'log_nofsync'           : log_nofsync,
+        'log_compress'          : log_compress,
+        'disable_gc'            : disable_gc,
+        'disable_snapshots'     : disable_snapshots,
+      }
+      print >>sys.stderr, '[INFO] running config %s' % (str(config))
+      if persist != PERSIST_NONE:
+        info = MACHINE_CONFIG[node]['logfiles']
+        tempprefix = MACHINE_CONFIG[node]['tempprefix']
+        logfiles = \
+            [x[0] for x in info] if persist == PERSIST_REAL \
+              else [os.path.join(tempprefix, 'data%d.log' % (idx)) for idx in xrange(len(info))]
+        weights = \
+          normalize([x[1] for x in info]) if persist == PERSIST_REAL else \
+          normalize([1.0 for _ in info])
+        assignments = allocate(threads, weights)
+      else:
+        logfiles, assignments = [], []
+      values = []
+      for _ in range(NTRIALS):
+        value = run_configuration(
+            binary, disable_madv_willneed,
+            basedir, db, bench, scale_factor, threads,
+            bench_opts, par_load, retry, backoff, numa_memory,
+            logfiles, assignments, log_fake_writes,
+            log_nofsync, log_compress, disable_gc,
+            disable_snapshots)
+        values.append(value)
+      results.append((config, values))
+
+    # write intermediate results
+    with open(outfile + '.py', 'w') as fp:
+      print >>fp, 'RESULTS = %s' % (repr(results))
+
+  # write results
+  with open(outfile + '.py', 'w') as fp:
+    print >>fp, 'RESULTS = %s' % (repr(results))
diff --git a/silo/benchmarks/stats_runner.py b/silo/benchmarks/stats_runner.py
new file mode 100644 (file)
index 0000000..48ebc01
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+import itertools as it
+import platform
+import math
+import subprocess
+import sys
+import time
+import multiprocessing as mp
+import os
+
+BUILDDIR='../out-perf.ectrs'
+if __name__ == '__main__':
+  (_, out) = sys.argv
+
+  args = [
+      os.path.join(BUILDDIR, 'benchmarks/dbtest'),
+      '--bench-opts', '--workload-mix 0,0,100,0',
+      '--stats-server-sockfile' , '/tmp/silo.sock',
+      '--num-threads', '28',
+      '--numa-memory', '96G',
+      '--scale-factor', '160000',
+      '--parallel-loading',
+      '--runtime', '30',
+  ]
+  env = dict(os.environ)
+  env['DISABLE_MADV_WILLNEED'] = '1'
+  p0 = subprocess.Popen(args, stdin=open('/dev/null', 'r'), stdout=open('/dev/null', 'w'), env=env)
+  time.sleep(1.0) # XXX: hacky
+  args = [os.path.join(BUILDDIR, 'stats_client'), '/tmp/silo.sock', 'dbtuple_bytes_allocated:dbtuple_bytes_freed']
+  with open(out, 'w') as fp:
+    p1 = subprocess.Popen(args, stdin=open('/dev/null', 'r'), stdout=fp)
+    p0.wait()
+    p1.wait()
diff --git a/silo/benchmarks/tpcc.cc b/silo/benchmarks/tpcc.cc
new file mode 100644 (file)
index 0000000..7619c4a
--- /dev/null
@@ -0,0 +1,2214 @@
+/**
+ * An implementation of TPC-C based off of:
+ * https://github.com/oltpbenchmark/oltpbench/tree/master/src/com/oltpbenchmark/benchmarks/tpcc
+ */
+
+#include <sys/time.h>
+#include <string>
+#include <ctype.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <set>
+#include <vector>
+
+#include "../txn.h"
+#include "../macros.h"
+#include "../scopedperf.hh"
+#include "../spinlock.h"
+
+#include "bench.h"
+#include "tpcc.h"
+using namespace std;
+using namespace util;
+
+#define TPCC_TABLE_LIST(x) \
+  x(customer) \
+  x(customer_name_idx) \
+  x(district) \
+  x(history) \
+  x(item) \
+  x(new_order) \
+  x(oorder) \
+  x(oorder_c_id_idx) \
+  x(order_line) \
+  x(stock) \
+  x(stock_data) \
+  x(warehouse)
+
+static inline ALWAYS_INLINE size_t
+NumWarehouses()
+{
+  return (size_t) scale_factor;
+}
+
+// config constants
+
+static constexpr inline ALWAYS_INLINE size_t
+NumItems()
+{
+  return 100000;
+}
+
+static constexpr inline ALWAYS_INLINE size_t
+NumDistrictsPerWarehouse()
+{
+  return 10;
+}
+
+static constexpr inline ALWAYS_INLINE size_t
+NumCustomersPerDistrict()
+{
+  return 3000;
+}
+
+// T must implement lock()/unlock(). Both must *not* throw exceptions
+template <typename T>
+class scoped_multilock {
+public:
+  inline scoped_multilock()
+    : did_lock(false)
+  {
+  }
+
+  inline ~scoped_multilock()
+  {
+    if (did_lock)
+      for (auto &t : locks)
+        t->unlock();
+  }
+
+  inline void
+  enq(T &t)
+  {
+    ALWAYS_ASSERT(!did_lock);
+    locks.emplace_back(&t);
+  }
+
+  inline void
+  multilock()
+  {
+    ALWAYS_ASSERT(!did_lock);
+    if (locks.size() > 1)
+      sort(locks.begin(), locks.end());
+#ifdef CHECK_INVARIANTS
+    if (set<T *>(locks.begin(), locks.end()).size() != locks.size()) {
+      for (auto &t : locks)
+        cerr << "lock: " << hexify(t) << endl;
+      INVARIANT(false && "duplicate locks found");
+    }
+#endif
+    for (auto &t : locks)
+      t->lock();
+    did_lock = true;
+  }
+
+private:
+  bool did_lock;
+  typename util::vec<T *, 64>::type locks;
+};
+
+// like a lock_guard, but has the option of not acquiring
+template <typename T>
+class scoped_lock_guard {
+public:
+  inline scoped_lock_guard(T &l)
+    : l(&l)
+  {
+    this->l->lock();
+  }
+
+  inline scoped_lock_guard(T *l)
+    : l(l)
+  {
+    if (this->l)
+      this->l->lock();
+  }
+
+  inline ~scoped_lock_guard()
+  {
+    if (l)
+      l->unlock();
+  }
+
+private:
+  T *l;
+};
+
+// configuration flags
+static int g_disable_xpartition_txn = 0;
+static int g_disable_read_only_scans = 0;
+static int g_enable_partition_locks = 0;
+static int g_enable_separate_tree_per_partition = 0;
+static int g_new_order_remote_item_pct = 1;
+static int g_new_order_fast_id_gen = 0;
+static int g_uniform_item_dist = 0;
+static int g_order_status_scan_hack = 0;
+static unsigned g_txn_workload_mix[] = { 45, 43, 4, 4, 4 }; // default TPC-C workload mix
+
+static aligned_padded_elem<spinlock> *g_partition_locks = nullptr;
+static aligned_padded_elem<atomic<uint64_t>> *g_district_ids = nullptr;
+
+// maps a wid => partition id
+static inline ALWAYS_INLINE unsigned int
+PartitionId(unsigned int wid)
+{
+  INVARIANT(wid >= 1 && wid <= NumWarehouses());
+  wid -= 1; // 0-idx
+  if (NumWarehouses() <= nthreads)
+    // more workers than partitions, so its easy
+    return wid;
+  const unsigned nwhse_per_partition = NumWarehouses() / nthreads;
+  const unsigned partid = wid / nwhse_per_partition;
+  if (partid >= nthreads)
+    return nthreads - 1;
+  return partid;
+}
+
+static inline ALWAYS_INLINE spinlock &
+LockForPartition(unsigned int wid)
+{
+  INVARIANT(g_enable_partition_locks);
+  return g_partition_locks[PartitionId(wid)].elem;
+}
+
+static inline atomic<uint64_t> &
+NewOrderIdHolder(unsigned warehouse, unsigned district)
+{
+  INVARIANT(warehouse >= 1 && warehouse <= NumWarehouses());
+  INVARIANT(district >= 1 && district <= NumDistrictsPerWarehouse());
+  const unsigned idx =
+    (warehouse - 1) * NumDistrictsPerWarehouse() + (district - 1);
+  return g_district_ids[idx].elem;
+}
+
+static inline uint64_t
+FastNewOrderIdGen(unsigned warehouse, unsigned district)
+{
+  return NewOrderIdHolder(warehouse, district).fetch_add(1, memory_order_acq_rel);
+}
+
+struct checker {
+  // these sanity checks are just a few simple checks to make sure
+  // the data is not entirely corrupted
+
+  static inline ALWAYS_INLINE void
+  SanityCheckCustomer(const customer::key *k, const customer::value *v)
+  {
+    INVARIANT(k->c_w_id >= 1 && static_cast<size_t>(k->c_w_id) <= NumWarehouses());
+    INVARIANT(k->c_d_id >= 1 && static_cast<size_t>(k->c_d_id) <= NumDistrictsPerWarehouse());
+    INVARIANT(k->c_id >= 1 && static_cast<size_t>(k->c_id) <= NumCustomersPerDistrict());
+    INVARIANT(v->c_credit == "BC" || v->c_credit == "GC");
+    INVARIANT(v->c_middle == "OE");
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckWarehouse(const warehouse::key *k, const warehouse::value *v)
+  {
+    INVARIANT(k->w_id >= 1 && static_cast<size_t>(k->w_id) <= NumWarehouses());
+    INVARIANT(v->w_state.size() == 2);
+    INVARIANT(v->w_zip == "123456789");
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckDistrict(const district::key *k, const district::value *v)
+  {
+    INVARIANT(k->d_w_id >= 1 && static_cast<size_t>(k->d_w_id) <= NumWarehouses());
+    INVARIANT(k->d_id >= 1 && static_cast<size_t>(k->d_id) <= NumDistrictsPerWarehouse());
+    INVARIANT(v->d_next_o_id >= 3001);
+    INVARIANT(v->d_state.size() == 2);
+    INVARIANT(v->d_zip == "123456789");
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckItem(const item::key *k, const item::value *v)
+  {
+    INVARIANT(k->i_id >= 1 && static_cast<size_t>(k->i_id) <= NumItems());
+    INVARIANT(v->i_price >= 1.0 && v->i_price <= 100.0);
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckStock(const stock::key *k, const stock::value *v)
+  {
+    INVARIANT(k->s_w_id >= 1 && static_cast<size_t>(k->s_w_id) <= NumWarehouses());
+    INVARIANT(k->s_i_id >= 1 && static_cast<size_t>(k->s_i_id) <= NumItems());
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckNewOrder(const new_order::key *k, const new_order::value *v)
+  {
+    INVARIANT(k->no_w_id >= 1 && static_cast<size_t>(k->no_w_id) <= NumWarehouses());
+    INVARIANT(k->no_d_id >= 1 && static_cast<size_t>(k->no_d_id) <= NumDistrictsPerWarehouse());
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckOOrder(const oorder::key *k, const oorder::value *v)
+  {
+    INVARIANT(k->o_w_id >= 1 && static_cast<size_t>(k->o_w_id) <= NumWarehouses());
+    INVARIANT(k->o_d_id >= 1 && static_cast<size_t>(k->o_d_id) <= NumDistrictsPerWarehouse());
+    INVARIANT(v->o_c_id >= 1 && static_cast<size_t>(v->o_c_id) <= NumCustomersPerDistrict());
+    INVARIANT(v->o_carrier_id >= 0 && static_cast<size_t>(v->o_carrier_id) <= NumDistrictsPerWarehouse());
+    INVARIANT(v->o_ol_cnt >= 5 && v->o_ol_cnt <= 15);
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckOrderLine(const order_line::key *k, const order_line::value *v)
+  {
+    INVARIANT(k->ol_w_id >= 1 && static_cast<size_t>(k->ol_w_id) <= NumWarehouses());
+    INVARIANT(k->ol_d_id >= 1 && static_cast<size_t>(k->ol_d_id) <= NumDistrictsPerWarehouse());
+    INVARIANT(k->ol_number >= 1 && k->ol_number <= 15);
+    INVARIANT(v->ol_i_id >= 1 && static_cast<size_t>(v->ol_i_id) <= NumItems());
+  }
+
+};
+
+
+struct _dummy {}; // exists so we can inherit from it, so we can use a macro in
+                  // an init list...
+
+class tpcc_worker_mixin : private _dummy {
+
+#define DEFN_TBL_INIT_X(name) \
+  , tbl_ ## name ## _vec(partitions.at(#name))
+
+public:
+  tpcc_worker_mixin(const map<string, vector<abstract_ordered_index *>> &partitions) :
+    _dummy() // so hacky...
+    TPCC_TABLE_LIST(DEFN_TBL_INIT_X)
+  {
+    ALWAYS_ASSERT(NumWarehouses() >= 1);
+  }
+
+#undef DEFN_TBL_INIT_X
+
+protected:
+
+#define DEFN_TBL_ACCESSOR_X(name) \
+private:  \
+  vector<abstract_ordered_index *> tbl_ ## name ## _vec; \
+protected: \
+  inline ALWAYS_INLINE abstract_ordered_index * \
+  tbl_ ## name (unsigned int wid) \
+  { \
+    INVARIANT(wid >= 1 && wid <= NumWarehouses()); \
+    INVARIANT(tbl_ ## name ## _vec.size() == NumWarehouses()); \
+    return tbl_ ## name ## _vec[wid - 1]; \
+  }
+
+  TPCC_TABLE_LIST(DEFN_TBL_ACCESSOR_X)
+
+#undef DEFN_TBL_ACCESSOR_X
+
+  // only TPCC loaders need to call this- workers are automatically
+  // pinned by their worker id (which corresponds to warehouse id
+  // in TPCC)
+  //
+  // pins the *calling* thread
+  static void
+  PinToWarehouseId(unsigned int wid)
+  {
+    const unsigned int partid = PartitionId(wid);
+    ALWAYS_ASSERT(partid < nthreads);
+    const unsigned int pinid  = partid;
+    if (verbose)
+      cerr << "PinToWarehouseId(): coreid=" << coreid::core_id()
+           << " pinned to whse=" << wid << " (partid=" << partid << ")"
+           << endl;
+    rcu::s_instance.pin_current_thread(pinid);
+    rcu::s_instance.fault_region();
+  }
+
+public:
+
+  static inline uint32_t
+  GetCurrentTimeMillis()
+  {
+    //struct timeval tv;
+    //ALWAYS_ASSERT(gettimeofday(&tv, 0) == 0);
+    //return tv.tv_sec * 1000;
+
+    // XXX(stephentu): implement a scalable GetCurrentTimeMillis()
+    // for now, we just give each core an increasing number
+
+    static __thread uint32_t tl_hack = 0;
+    return tl_hack++;
+  }
+
+  // utils for generating random #s and strings
+
+  static inline ALWAYS_INLINE int
+  CheckBetweenInclusive(int v, int lower, int upper)
+  {
+    INVARIANT(v >= lower);
+    INVARIANT(v <= upper);
+    return v;
+  }
+
+  static inline ALWAYS_INLINE int
+  RandomNumber(fast_random &r, int min, int max)
+  {
+    return CheckBetweenInclusive((int) (r.next_uniform() * (max - min + 1) + min), min, max);
+  }
+
+  static inline ALWAYS_INLINE int
+  NonUniformRandom(fast_random &r, int A, int C, int min, int max)
+  {
+    return (((RandomNumber(r, 0, A) | RandomNumber(r, min, max)) + C) % (max - min + 1)) + min;
+  }
+
+  static inline ALWAYS_INLINE int
+  GetItemId(fast_random &r)
+  {
+    return CheckBetweenInclusive(
+        g_uniform_item_dist ?
+          RandomNumber(r, 1, NumItems()) :
+          NonUniformRandom(r, 8191, 7911, 1, NumItems()),
+        1, NumItems());
+  }
+
+  static inline ALWAYS_INLINE int
+  GetCustomerId(fast_random &r)
+  {
+    return CheckBetweenInclusive(NonUniformRandom(r, 1023, 259, 1, NumCustomersPerDistrict()), 1, NumCustomersPerDistrict());
+  }
+
+  // pick a number between [start, end)
+  static inline ALWAYS_INLINE unsigned
+  PickWarehouseId(fast_random &r, unsigned start, unsigned end)
+  {
+    INVARIANT(start < end);
+    const unsigned diff = end - start;
+    if (diff == 1)
+      return start;
+    return (r.next() % diff) + start;
+  }
+
+  static string NameTokens[];
+
+  // all tokens are at most 5 chars long
+  static const size_t CustomerLastNameMaxSize = 5 * 3;
+
+  static inline size_t
+  GetCustomerLastName(uint8_t *buf, fast_random &r, int num)
+  {
+    const string &s0 = NameTokens[num / 100];
+    const string &s1 = NameTokens[(num / 10) % 10];
+    const string &s2 = NameTokens[num % 10];
+    uint8_t *const begin = buf;
+    const size_t s0_sz = s0.size();
+    const size_t s1_sz = s1.size();
+    const size_t s2_sz = s2.size();
+    NDB_MEMCPY(buf, s0.data(), s0_sz); buf += s0_sz;
+    NDB_MEMCPY(buf, s1.data(), s1_sz); buf += s1_sz;
+    NDB_MEMCPY(buf, s2.data(), s2_sz); buf += s2_sz;
+    return buf - begin;
+  }
+
+  static inline ALWAYS_INLINE size_t
+  GetCustomerLastName(char *buf, fast_random &r, int num)
+  {
+    return GetCustomerLastName((uint8_t *) buf, r, num);
+  }
+
+  static inline string
+  GetCustomerLastName(fast_random &r, int num)
+  {
+    string ret;
+    ret.resize(CustomerLastNameMaxSize);
+    ret.resize(GetCustomerLastName((uint8_t *) &ret[0], r, num));
+    return ret;
+  }
+
+  static inline ALWAYS_INLINE string
+  GetNonUniformCustomerLastNameLoad(fast_random &r)
+  {
+    return GetCustomerLastName(r, NonUniformRandom(r, 255, 157, 0, 999));
+  }
+
+  static inline ALWAYS_INLINE size_t
+  GetNonUniformCustomerLastNameRun(uint8_t *buf, fast_random &r)
+  {
+    return GetCustomerLastName(buf, r, NonUniformRandom(r, 255, 223, 0, 999));
+  }
+
+  static inline ALWAYS_INLINE size_t
+  GetNonUniformCustomerLastNameRun(char *buf, fast_random &r)
+  {
+    return GetNonUniformCustomerLastNameRun((uint8_t *) buf, r);
+  }
+
+  static inline ALWAYS_INLINE string
+  GetNonUniformCustomerLastNameRun(fast_random &r)
+  {
+    return GetCustomerLastName(r, NonUniformRandom(r, 255, 223, 0, 999));
+  }
+
+  // following oltpbench, we really generate strings of len - 1...
+  static inline string
+  RandomStr(fast_random &r, uint len)
+  {
+    // this is a property of the oltpbench implementation...
+    if (!len)
+      return "";
+
+    uint i = 0;
+    string buf(len - 1, 0);
+    while (i < (len - 1)) {
+      const char c = (char) r.next_char();
+      // XXX(stephentu): oltpbench uses java's Character.isLetter(), which
+      // is a less restrictive filter than isalnum()
+      if (!isalnum(c))
+        continue;
+      buf[i++] = c;
+    }
+    return buf;
+  }
+
+  // RandomNStr() actually produces a string of length len
+  static inline string
+  RandomNStr(fast_random &r, uint len)
+  {
+    const char base = '0';
+    string buf(len, 0);
+    for (uint i = 0; i < len; i++)
+      buf[i] = (char)(base + (r.next() % 10));
+    return buf;
+  }
+};
+
+string tpcc_worker_mixin::NameTokens[] =
+  {
+    string("BAR"),
+    string("OUGHT"),
+    string("ABLE"),
+    string("PRI"),
+    string("PRES"),
+    string("ESE"),
+    string("ANTI"),
+    string("CALLY"),
+    string("ATION"),
+    string("EING"),
+  };
+
+STATIC_COUNTER_DECL(scopedperf::tsc_ctr, tpcc_txn, tpcc_txn_cg)
+
+class tpcc_worker : public bench_worker, public tpcc_worker_mixin {
+public:
+  // resp for [warehouse_id_start, warehouse_id_end)
+  tpcc_worker(unsigned int worker_id,
+              unsigned long seed, abstract_db *db,
+              const map<string, abstract_ordered_index *> &open_tables,
+              const map<string, vector<abstract_ordered_index *>> &partitions,
+              spin_barrier *barrier_a, spin_barrier *barrier_b,
+              uint warehouse_id_start, uint warehouse_id_end)
+    : bench_worker(worker_id, true, seed, db,
+                   open_tables, barrier_a, barrier_b),
+      tpcc_worker_mixin(partitions),
+      warehouse_id_start(warehouse_id_start),
+      warehouse_id_end(warehouse_id_end)
+  {
+    INVARIANT(warehouse_id_start >= 1);
+    INVARIANT(warehouse_id_start <= NumWarehouses());
+    INVARIANT(warehouse_id_end > warehouse_id_start);
+    INVARIANT(warehouse_id_end <= (NumWarehouses() + 1));
+    NDB_MEMSET(&last_no_o_ids[0], 0, sizeof(last_no_o_ids));
+    if (verbose) {
+      cerr << "tpcc: worker id " << worker_id
+        << " => warehouses [" << warehouse_id_start
+        << ", " << warehouse_id_end << ")"
+        << endl;
+    }
+    obj_key0.reserve(str_arena::MinStrReserveLength);
+    obj_key1.reserve(str_arena::MinStrReserveLength);
+    obj_v.reserve(str_arena::MinStrReserveLength);
+  }
+
+  // XXX(stephentu): tune this
+  static const size_t NMaxCustomerIdxScanElems = 512;
+
+  txn_result txn_new_order();
+
+  static txn_result
+  TxnNewOrder(bench_worker *w)
+  {
+    ANON_REGION("TxnNewOrder:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_new_order();
+  }
+
+  txn_result txn_delivery();
+
+  static txn_result
+  TxnDelivery(bench_worker *w)
+  {
+    ANON_REGION("TxnDelivery:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_delivery();
+  }
+
+  txn_result txn_payment();
+
+  static txn_result
+  TxnPayment(bench_worker *w)
+  {
+    ANON_REGION("TxnPayment:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_payment();
+  }
+
+  txn_result txn_order_status();
+
+  static txn_result
+  TxnOrderStatus(bench_worker *w)
+  {
+    ANON_REGION("TxnOrderStatus:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_order_status();
+  }
+
+  txn_result txn_stock_level();
+
+  static txn_result
+  TxnStockLevel(bench_worker *w)
+  {
+    ANON_REGION("TxnStockLevel:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_stock_level();
+  }
+
+  virtual workload_desc_vec
+  get_workload() const
+  {
+    workload_desc_vec w;
+    // numbers from sigmod.csail.mit.edu:
+    //w.push_back(workload_desc("NewOrder", 1.0, TxnNewOrder)); // ~10k ops/sec
+    //w.push_back(workload_desc("Payment", 1.0, TxnPayment)); // ~32k ops/sec
+    //w.push_back(workload_desc("Delivery", 1.0, TxnDelivery)); // ~104k ops/sec
+    //w.push_back(workload_desc("OrderStatus", 1.0, TxnOrderStatus)); // ~33k ops/sec
+    //w.push_back(workload_desc("StockLevel", 1.0, TxnStockLevel)); // ~2k ops/sec
+    unsigned m = 0;
+    for (size_t i = 0; i < ARRAY_NELEMS(g_txn_workload_mix); i++)
+      m += g_txn_workload_mix[i];
+    ALWAYS_ASSERT(m == 100);
+    if (g_txn_workload_mix[0])
+      w.push_back(workload_desc("NewOrder", double(g_txn_workload_mix[0])/100.0, TxnNewOrder));
+    if (g_txn_workload_mix[1])
+      w.push_back(workload_desc("Payment", double(g_txn_workload_mix[1])/100.0, TxnPayment));
+    if (g_txn_workload_mix[2])
+      w.push_back(workload_desc("Delivery", double(g_txn_workload_mix[2])/100.0, TxnDelivery));
+    if (g_txn_workload_mix[3])
+      w.push_back(workload_desc("OrderStatus", double(g_txn_workload_mix[3])/100.0, TxnOrderStatus));
+    if (g_txn_workload_mix[4])
+      w.push_back(workload_desc("StockLevel", double(g_txn_workload_mix[4])/100.0, TxnStockLevel));
+    return w;
+  }
+
+protected:
+
+  virtual void
+  on_run_setup() OVERRIDE
+  {
+    if (!pin_cpus)
+      return;
+    const size_t a = worker_id % coreid::num_cpus_online();
+    const size_t b = a % nthreads;
+    rcu::s_instance.pin_current_thread(b);
+    rcu::s_instance.fault_region();
+  }
+
+  inline ALWAYS_INLINE string &
+  str()
+  {
+    return *arena.next();
+  }
+
+private:
+  const uint warehouse_id_start;
+  const uint warehouse_id_end;
+  int32_t last_no_o_ids[10]; // XXX(stephentu): hack
+
+  // some scratch buffer space
+  string obj_key0;
+  string obj_key1;
+  string obj_v;
+};
+
+class tpcc_warehouse_loader : public bench_loader, public tpcc_worker_mixin {
+public:
+  tpcc_warehouse_loader(unsigned long seed,
+                        abstract_db *db,
+                        const map<string, abstract_ordered_index *> &open_tables,
+                        const map<string, vector<abstract_ordered_index *>> &partitions)
+    : bench_loader(seed, db, open_tables),
+      tpcc_worker_mixin(partitions)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    string obj_buf;
+    void *txn = db->new_txn(txn_flags, arena, txn_buf());
+    uint64_t warehouse_total_sz = 0, n_warehouses = 0;
+    try {
+      vector<warehouse::value> warehouses;
+      for (uint i = 1; i <= NumWarehouses(); i++) {
+        const warehouse::key k(i);
+
+        const string w_name = RandomStr(r, RandomNumber(r, 6, 10));
+        const string w_street_1 = RandomStr(r, RandomNumber(r, 10, 20));
+        const string w_street_2 = RandomStr(r, RandomNumber(r, 10, 20));
+        const string w_city = RandomStr(r, RandomNumber(r, 10, 20));
+        const string w_state = RandomStr(r, 3);
+        const string w_zip = "123456789";
+
+        warehouse::value v;
+        v.w_ytd = 300000;
+        v.w_tax = (float) RandomNumber(r, 0, 2000) / 10000.0;
+        v.w_name.assign(w_name);
+        v.w_street_1.assign(w_street_1);
+        v.w_street_2.assign(w_street_2);
+        v.w_city.assign(w_city);
+        v.w_state.assign(w_state);
+        v.w_zip.assign(w_zip);
+
+        checker::SanityCheckWarehouse(&k, &v);
+        const size_t sz = Size(v);
+        warehouse_total_sz += sz;
+        n_warehouses++;
+        tbl_warehouse(i)->insert(txn, Encode(k), Encode(obj_buf, v));
+
+        warehouses.push_back(v);
+      }
+      ALWAYS_ASSERT(db->commit_txn(txn));
+      arena.reset();
+      txn = db->new_txn(txn_flags, arena, txn_buf());
+      for (uint i = 1; i <= NumWarehouses(); i++) {
+        const warehouse::key k(i);
+        string warehouse_v;
+        ALWAYS_ASSERT(tbl_warehouse(i)->get(txn, Encode(k), warehouse_v));
+        warehouse::value warehouse_temp;
+        const warehouse::value *v = Decode(warehouse_v, warehouse_temp);
+        ALWAYS_ASSERT(warehouses[i - 1] == *v);
+
+        checker::SanityCheckWarehouse(&k, v);
+      }
+      ALWAYS_ASSERT(db->commit_txn(txn));
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      // shouldn't abort on loading!
+      ALWAYS_ASSERT(false);
+    }
+    if (verbose) {
+      cerr << "[INFO] finished loading warehouse" << endl;
+      cerr << "[INFO]   * average warehouse record length: "
+           << (double(warehouse_total_sz)/double(n_warehouses)) << " bytes" << endl;
+    }
+  }
+};
+
+class tpcc_item_loader : public bench_loader, public tpcc_worker_mixin {
+public:
+  tpcc_item_loader(unsigned long seed,
+                   abstract_db *db,
+                   const map<string, abstract_ordered_index *> &open_tables,
+                   const map<string, vector<abstract_ordered_index *>> &partitions)
+    : bench_loader(seed, db, open_tables),
+      tpcc_worker_mixin(partitions)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    string obj_buf;
+    const ssize_t bsize = db->txn_max_batch_size();
+    void *txn = db->new_txn(txn_flags, arena, txn_buf());
+    uint64_t total_sz = 0;
+    try {
+      for (uint i = 1; i <= NumItems(); i++) {
+        // items don't "belong" to a certain warehouse, so no pinning
+        const item::key k(i);
+
+        item::value v;
+        const string i_name = RandomStr(r, RandomNumber(r, 14, 24));
+        v.i_name.assign(i_name);
+        v.i_price = (float) RandomNumber(r, 100, 10000) / 100.0;
+        const int len = RandomNumber(r, 26, 50);
+        if (RandomNumber(r, 1, 100) > 10) {
+          const string i_data = RandomStr(r, len);
+          v.i_data.assign(i_data);
+        } else {
+          const int startOriginal = RandomNumber(r, 2, (len - 8));
+          const string i_data = RandomStr(r, startOriginal + 1) + "ORIGINAL" + RandomStr(r, len - startOriginal - 7);
+          v.i_data.assign(i_data);
+        }
+        v.i_im_id = RandomNumber(r, 1, 10000);
+
+        checker::SanityCheckItem(&k, &v);
+        const size_t sz = Size(v);
+        total_sz += sz;
+        tbl_item(1)->insert(txn, Encode(k), Encode(obj_buf, v)); // this table is shared, so any partition is OK
+
+        if (bsize != -1 && !(i % bsize)) {
+          ALWAYS_ASSERT(db->commit_txn(txn));
+          txn = db->new_txn(txn_flags, arena, txn_buf());
+          arena.reset();
+        }
+      }
+      ALWAYS_ASSERT(db->commit_txn(txn));
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      // shouldn't abort on loading!
+      ALWAYS_ASSERT(false);
+    }
+    if (verbose) {
+      cerr << "[INFO] finished loading item" << endl;
+      cerr << "[INFO]   * average item record length: "
+           << (double(total_sz)/double(NumItems())) << " bytes" << endl;
+    }
+  }
+};
+
+class tpcc_stock_loader : public bench_loader, public tpcc_worker_mixin {
+public:
+  tpcc_stock_loader(unsigned long seed,
+                    abstract_db *db,
+                    const map<string, abstract_ordered_index *> &open_tables,
+                    const map<string, vector<abstract_ordered_index *>> &partitions,
+                    ssize_t warehouse_id)
+    : bench_loader(seed, db, open_tables),
+      tpcc_worker_mixin(partitions),
+      warehouse_id(warehouse_id)
+  {
+    ALWAYS_ASSERT(warehouse_id == -1 ||
+                  (warehouse_id >= 1 &&
+                   static_cast<size_t>(warehouse_id) <= NumWarehouses()));
+  }
+
+protected:
+  virtual void
+  load()
+  {
+    string obj_buf, obj_buf1;
+
+    uint64_t stock_total_sz = 0, n_stocks = 0;
+    const uint w_start = (warehouse_id == -1) ?
+      1 : static_cast<uint>(warehouse_id);
+    const uint w_end   = (warehouse_id == -1) ?
+      NumWarehouses() : static_cast<uint>(warehouse_id);
+
+    for (uint w = w_start; w <= w_end; w++) {
+      const size_t batchsize =
+        (db->txn_max_batch_size() == -1) ? NumItems() : db->txn_max_batch_size();
+      const size_t nbatches = (batchsize > NumItems()) ? 1 : (NumItems() / batchsize);
+
+      if (pin_cpus)
+        PinToWarehouseId(w);
+
+      for (uint b = 0; b < nbatches;) {
+        scoped_str_arena s_arena(arena);
+        void * const txn = db->new_txn(txn_flags, arena, txn_buf());
+        try {
+          const size_t iend = std::min((b + 1) * batchsize + 1, NumItems());
+          for (uint i = (b * batchsize + 1); i <= iend; i++) {
+            const stock::key k(w, i);
+            const stock_data::key k_data(w, i);
+
+            stock::value v;
+            v.s_quantity = RandomNumber(r, 10, 100);
+            v.s_ytd = 0;
+            v.s_order_cnt = 0;
+            v.s_remote_cnt = 0;
+
+            stock_data::value v_data;
+            const int len = RandomNumber(r, 26, 50);
+            if (RandomNumber(r, 1, 100) > 10) {
+              const string s_data = RandomStr(r, len);
+              v_data.s_data.assign(s_data);
+            } else {
+              const int startOriginal = RandomNumber(r, 2, (len - 8));
+              const string s_data = RandomStr(r, startOriginal + 1) + "ORIGINAL" + RandomStr(r, len - startOriginal - 7);
+              v_data.s_data.assign(s_data);
+            }
+            v_data.s_dist_01.assign(RandomStr(r, 24));
+            v_data.s_dist_02.assign(RandomStr(r, 24));
+            v_data.s_dist_03.assign(RandomStr(r, 24));
+            v_data.s_dist_04.assign(RandomStr(r, 24));
+            v_data.s_dist_05.assign(RandomStr(r, 24));
+            v_data.s_dist_06.assign(RandomStr(r, 24));
+            v_data.s_dist_07.assign(RandomStr(r, 24));
+            v_data.s_dist_08.assign(RandomStr(r, 24));
+            v_data.s_dist_09.assign(RandomStr(r, 24));
+            v_data.s_dist_10.assign(RandomStr(r, 24));
+
+            checker::SanityCheckStock(&k, &v);
+            const size_t sz = Size(v);
+            stock_total_sz += sz;
+            n_stocks++;
+            tbl_stock(w)->insert(txn, Encode(k), Encode(obj_buf, v));
+            tbl_stock_data(w)->insert(txn, Encode(k_data), Encode(obj_buf1, v_data));
+          }
+          if (db->commit_txn(txn)) {
+            b++;
+          } else {
+            db->abort_txn(txn);
+            if (verbose)
+              cerr << "[WARNING] stock loader loading abort" << endl;
+          }
+        } catch (abstract_db::abstract_abort_exception &ex) {
+          db->abort_txn(txn);
+          ALWAYS_ASSERT(warehouse_id != -1);
+          if (verbose)
+            cerr << "[WARNING] stock loader loading abort" << endl;
+        }
+      }
+    }
+
+    if (verbose) {
+      if (warehouse_id == -1) {
+        cerr << "[INFO] finished loading stock" << endl;
+        cerr << "[INFO]   * average stock record length: "
+             << (double(stock_total_sz)/double(n_stocks)) << " bytes" << endl;
+      } else {
+        cerr << "[INFO] finished loading stock (w=" << warehouse_id << ")" << endl;
+      }
+    }
+  }
+
+private:
+  ssize_t warehouse_id;
+};
+
+class tpcc_district_loader : public bench_loader, public tpcc_worker_mixin {
+public:
+  tpcc_district_loader(unsigned long seed,
+                       abstract_db *db,
+                       const map<string, abstract_ordered_index *> &open_tables,
+                       const map<string, vector<abstract_ordered_index *>> &partitions)
+    : bench_loader(seed, db, open_tables),
+      tpcc_worker_mixin(partitions)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    string obj_buf;
+
+    const ssize_t bsize = db->txn_max_batch_size();
+    void *txn = db->new_txn(txn_flags, arena, txn_buf());
+    uint64_t district_total_sz = 0, n_districts = 0;
+    try {
+      uint cnt = 0;
+      for (uint w = 1; w <= NumWarehouses(); w++) {
+        if (pin_cpus)
+          PinToWarehouseId(w);
+        for (uint d = 1; d <= NumDistrictsPerWarehouse(); d++, cnt++) {
+          const district::key k(w, d);
+
+          district::value v;
+          v.d_ytd = 30000;
+          v.d_tax = (float) (RandomNumber(r, 0, 2000) / 10000.0);
+          v.d_next_o_id = 3001;
+          v.d_name.assign(RandomStr(r, RandomNumber(r, 6, 10)));
+          v.d_street_1.assign(RandomStr(r, RandomNumber(r, 10, 20)));
+          v.d_street_2.assign(RandomStr(r, RandomNumber(r, 10, 20)));
+          v.d_city.assign(RandomStr(r, RandomNumber(r, 10, 20)));
+          v.d_state.assign(RandomStr(r, 3));
+          v.d_zip.assign("123456789");
+
+          checker::SanityCheckDistrict(&k, &v);
+          const size_t sz = Size(v);
+          district_total_sz += sz;
+          n_districts++;
+          tbl_district(w)->insert(txn, Encode(k), Encode(obj_buf, v));
+
+          if (bsize != -1 && !((cnt + 1) % bsize)) {
+            ALWAYS_ASSERT(db->commit_txn(txn));
+            txn = db->new_txn(txn_flags, arena, txn_buf());
+            arena.reset();
+          }
+        }
+      }
+      ALWAYS_ASSERT(db->commit_txn(txn));
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      // shouldn't abort on loading!
+      ALWAYS_ASSERT(false);
+    }
+    if (verbose) {
+      cerr << "[INFO] finished loading district" << endl;
+      cerr << "[INFO]   * average district record length: "
+           << (double(district_total_sz)/double(n_districts)) << " bytes" << endl;
+    }
+  }
+};
+
+class tpcc_customer_loader : public bench_loader, public tpcc_worker_mixin {
+public:
+  tpcc_customer_loader(unsigned long seed,
+                       abstract_db *db,
+                       const map<string, abstract_ordered_index *> &open_tables,
+                       const map<string, vector<abstract_ordered_index *>> &partitions,
+                       ssize_t warehouse_id)
+    : bench_loader(seed, db, open_tables),
+      tpcc_worker_mixin(partitions),
+      warehouse_id(warehouse_id)
+  {
+    ALWAYS_ASSERT(warehouse_id == -1 ||
+                  (warehouse_id >= 1 &&
+                   static_cast<size_t>(warehouse_id) <= NumWarehouses()));
+  }
+
+protected:
+  virtual void
+  load()
+  {
+    string obj_buf;
+
+    const uint w_start = (warehouse_id == -1) ?
+      1 : static_cast<uint>(warehouse_id);
+    const uint w_end   = (warehouse_id == -1) ?
+      NumWarehouses() : static_cast<uint>(warehouse_id);
+    const size_t batchsize =
+      (db->txn_max_batch_size() == -1) ?
+        NumCustomersPerDistrict() : db->txn_max_batch_size();
+    const size_t nbatches =
+      (batchsize > NumCustomersPerDistrict()) ?
+        1 : (NumCustomersPerDistrict() / batchsize);
+    cerr << "num batches: " << nbatches << endl;
+
+    uint64_t total_sz = 0;
+
+    for (uint w = w_start; w <= w_end; w++) {
+      if (pin_cpus)
+        PinToWarehouseId(w);
+      for (uint d = 1; d <= NumDistrictsPerWarehouse(); d++) {
+        for (uint batch = 0; batch < nbatches;) {
+          scoped_str_arena s_arena(arena);
+          void * const txn = db->new_txn(txn_flags, arena, txn_buf());
+          const size_t cstart = batch * batchsize;
+          const size_t cend = std::min((batch + 1) * batchsize, NumCustomersPerDistrict());
+          try {
+            for (uint cidx0 = cstart; cidx0 < cend; cidx0++) {
+              const uint c = cidx0 + 1;
+              const customer::key k(w, d, c);
+
+              customer::value v;
+              v.c_discount = (float) (RandomNumber(r, 1, 5000) / 10000.0);
+              if (RandomNumber(r, 1, 100) <= 10)
+                v.c_credit.assign("BC");
+              else
+                v.c_credit.assign("GC");
+
+              if (c <= 1000)
+                v.c_last.assign(GetCustomerLastName(r, c - 1));
+              else
+                v.c_last.assign(GetNonUniformCustomerLastNameLoad(r));
+
+              v.c_first.assign(RandomStr(r, RandomNumber(r, 8, 16)));
+              v.c_credit_lim = 50000;
+
+              v.c_balance = -10;
+              v.c_ytd_payment = 10;
+              v.c_payment_cnt = 1;
+              v.c_delivery_cnt = 0;
+
+              v.c_street_1.assign(RandomStr(r, RandomNumber(r, 10, 20)));
+              v.c_street_2.assign(RandomStr(r, RandomNumber(r, 10, 20)));
+              v.c_city.assign(RandomStr(r, RandomNumber(r, 10, 20)));
+              v.c_state.assign(RandomStr(r, 3));
+              v.c_zip.assign(RandomNStr(r, 4) + "11111");
+              v.c_phone.assign(RandomNStr(r, 16));
+              v.c_since = GetCurrentTimeMillis();
+              v.c_middle.assign("OE");
+              v.c_data.assign(RandomStr(r, RandomNumber(r, 300, 500)));
+
+              checker::SanityCheckCustomer(&k, &v);
+              const size_t sz = Size(v);
+              total_sz += sz;
+              tbl_customer(w)->insert(txn, Encode(k), Encode(obj_buf, v));
+
+              // customer name index
+              const customer_name_idx::key k_idx(k.c_w_id, k.c_d_id, v.c_last.str(true), v.c_first.str(true));
+              const customer_name_idx::value v_idx(k.c_id);
+
+              // index structure is:
+              // (c_w_id, c_d_id, c_last, c_first) -> (c_id)
+
+              tbl_customer_name_idx(w)->insert(txn, Encode(k_idx), Encode(obj_buf, v_idx));
+
+              history::key k_hist;
+              k_hist.h_c_id = c;
+              k_hist.h_c_d_id = d;
+              k_hist.h_c_w_id = w;
+              k_hist.h_d_id = d;
+              k_hist.h_w_id = w;
+              k_hist.h_date = GetCurrentTimeMillis();
+
+              history::value v_hist;
+              v_hist.h_amount = 10;
+              v_hist.h_data.assign(RandomStr(r, RandomNumber(r, 10, 24)));
+
+              tbl_history(w)->insert(txn, Encode(k_hist), Encode(obj_buf, v_hist));
+            }
+            if (db->commit_txn(txn)) {
+              batch++;
+            } else {
+              db->abort_txn(txn);
+              if (verbose)
+                cerr << "[WARNING] customer loader loading abort" << endl;
+            }
+          } catch (abstract_db::abstract_abort_exception &ex) {
+            db->abort_txn(txn);
+            if (verbose)
+              cerr << "[WARNING] customer loader loading abort" << endl;
+          }
+        }
+      }
+    }
+
+    if (verbose) {
+      if (warehouse_id == -1) {
+        cerr << "[INFO] finished loading customer" << endl;
+        cerr << "[INFO]   * average customer record length: "
+             << (double(total_sz)/double(NumWarehouses()*NumDistrictsPerWarehouse()*NumCustomersPerDistrict()))
+             << " bytes " << endl;
+      } else {
+        cerr << "[INFO] finished loading customer (w=" << warehouse_id << ")" << endl;
+      }
+    }
+  }
+
+private:
+  ssize_t warehouse_id;
+};
+
+class tpcc_order_loader : public bench_loader, public tpcc_worker_mixin {
+public:
+  tpcc_order_loader(unsigned long seed,
+                    abstract_db *db,
+                    const map<string, abstract_ordered_index *> &open_tables,
+                    const map<string, vector<abstract_ordered_index *>> &partitions,
+                    ssize_t warehouse_id)
+    : bench_loader(seed, db, open_tables),
+      tpcc_worker_mixin(partitions),
+      warehouse_id(warehouse_id)
+  {
+    ALWAYS_ASSERT(warehouse_id == -1 ||
+                  (warehouse_id >= 1 &&
+                   static_cast<size_t>(warehouse_id) <= NumWarehouses()));
+  }
+
+protected:
+  virtual void
+  load()
+  {
+    string obj_buf;
+
+    uint64_t order_line_total_sz = 0, n_order_lines = 0;
+    uint64_t oorder_total_sz = 0, n_oorders = 0;
+    uint64_t new_order_total_sz = 0, n_new_orders = 0;
+
+    const uint w_start = (warehouse_id == -1) ?
+      1 : static_cast<uint>(warehouse_id);
+    const uint w_end   = (warehouse_id == -1) ?
+      NumWarehouses() : static_cast<uint>(warehouse_id);
+
+    for (uint w = w_start; w <= w_end; w++) {
+      if (pin_cpus)
+        PinToWarehouseId(w);
+      for (uint d = 1; d <= NumDistrictsPerWarehouse(); d++) {
+        set<uint> c_ids_s;
+        vector<uint> c_ids;
+        while (c_ids.size() != NumCustomersPerDistrict()) {
+          const auto x = (r.next() % NumCustomersPerDistrict()) + 1;
+          if (c_ids_s.count(x))
+            continue;
+          c_ids_s.insert(x);
+          c_ids.emplace_back(x);
+        }
+        for (uint c = 1; c <= NumCustomersPerDistrict();) {
+          scoped_str_arena s_arena(arena);
+          void * const txn = db->new_txn(txn_flags, arena, txn_buf());
+          try {
+            const oorder::key k_oo(w, d, c);
+
+            oorder::value v_oo;
+            v_oo.o_c_id = c_ids[c - 1];
+            if (k_oo.o_id < 2101)
+              v_oo.o_carrier_id = RandomNumber(r, 1, 10);
+            else
+              v_oo.o_carrier_id = 0;
+            v_oo.o_ol_cnt = RandomNumber(r, 5, 15);
+            v_oo.o_all_local = 1;
+            v_oo.o_entry_d = GetCurrentTimeMillis();
+
+            checker::SanityCheckOOrder(&k_oo, &v_oo);
+            const size_t sz = Size(v_oo);
+            oorder_total_sz += sz;
+            n_oorders++;
+            tbl_oorder(w)->insert(txn, Encode(k_oo), Encode(obj_buf, v_oo));
+
+            const oorder_c_id_idx::key k_oo_idx(k_oo.o_w_id, k_oo.o_d_id, v_oo.o_c_id, k_oo.o_id);
+            const oorder_c_id_idx::value v_oo_idx(0);
+
+            tbl_oorder_c_id_idx(w)->insert(txn, Encode(k_oo_idx), Encode(obj_buf, v_oo_idx));
+
+            if (c >= 2101) {
+              const new_order::key k_no(w, d, c);
+              const new_order::value v_no;
+
+              checker::SanityCheckNewOrder(&k_no, &v_no);
+              const size_t sz = Size(v_no);
+              new_order_total_sz += sz;
+              n_new_orders++;
+              tbl_new_order(w)->insert(txn, Encode(k_no), Encode(obj_buf, v_no));
+            }
+
+            for (uint l = 1; l <= uint(v_oo.o_ol_cnt); l++) {
+              const order_line::key k_ol(w, d, c, l);
+
+              order_line::value v_ol;
+              v_ol.ol_i_id = RandomNumber(r, 1, 100000);
+              if (k_ol.ol_o_id < 2101) {
+                v_ol.ol_delivery_d = v_oo.o_entry_d;
+                v_ol.ol_amount = 0;
+              } else {
+                v_ol.ol_delivery_d = 0;
+                // random within [0.01 .. 9,999.99]
+                v_ol.ol_amount = (float) (RandomNumber(r, 1, 999999) / 100.0);
+              }
+
+              v_ol.ol_supply_w_id = k_ol.ol_w_id;
+              v_ol.ol_quantity = 5;
+              // v_ol.ol_dist_info comes from stock_data(ol_supply_w_id, ol_o_id)
+              //v_ol.ol_dist_info = RandomStr(r, 24);
+
+              checker::SanityCheckOrderLine(&k_ol, &v_ol);
+              const size_t sz = Size(v_ol);
+              order_line_total_sz += sz;
+              n_order_lines++;
+              tbl_order_line(w)->insert(txn, Encode(k_ol), Encode(obj_buf, v_ol));
+            }
+            if (db->commit_txn(txn)) {
+              c++;
+            } else {
+              db->abort_txn(txn);
+              ALWAYS_ASSERT(warehouse_id != -1);
+              if (verbose)
+                cerr << "[WARNING] order loader loading abort" << endl;
+            }
+          } catch (abstract_db::abstract_abort_exception &ex) {
+            db->abort_txn(txn);
+            ALWAYS_ASSERT(warehouse_id != -1);
+            if (verbose)
+              cerr << "[WARNING] order loader loading abort" << endl;
+          }
+        }
+      }
+    }
+
+    if (verbose) {
+      if (warehouse_id == -1) {
+        cerr << "[INFO] finished loading order" << endl;
+        cerr << "[INFO]   * average order_line record length: "
+             << (double(order_line_total_sz)/double(n_order_lines)) << " bytes" << endl;
+        cerr << "[INFO]   * average oorder record length: "
+             << (double(oorder_total_sz)/double(n_oorders)) << " bytes" << endl;
+        cerr << "[INFO]   * average new_order record length: "
+             << (double(new_order_total_sz)/double(n_new_orders)) << " bytes" << endl;
+      } else {
+        cerr << "[INFO] finished loading order (w=" << warehouse_id << ")" << endl;
+      }
+    }
+  }
+
+private:
+  ssize_t warehouse_id;
+};
+
+static event_counter evt_tpcc_cross_partition_new_order_txns("tpcc_cross_partition_new_order_txns");
+static event_counter evt_tpcc_cross_partition_payment_txns("tpcc_cross_partition_payment_txns");
+
+tpcc_worker::txn_result
+tpcc_worker::txn_new_order()
+{
+  const uint warehouse_id = PickWarehouseId(r, warehouse_id_start, warehouse_id_end);
+  const uint districtID = RandomNumber(r, 1, 10);
+  const uint customerID = GetCustomerId(r);
+  const uint numItems = RandomNumber(r, 5, 15);
+  uint itemIDs[15], supplierWarehouseIDs[15], orderQuantities[15];
+  bool allLocal = true;
+  for (uint i = 0; i < numItems; i++) {
+    itemIDs[i] = GetItemId(r);
+    if (likely(g_disable_xpartition_txn ||
+               NumWarehouses() == 1 ||
+               RandomNumber(r, 1, 100) > g_new_order_remote_item_pct)) {
+      supplierWarehouseIDs[i] = warehouse_id;
+    } else {
+      do {
+       supplierWarehouseIDs[i] = RandomNumber(r, 1, NumWarehouses());
+      } while (supplierWarehouseIDs[i] == warehouse_id);
+      allLocal = false;
+    }
+    orderQuantities[i] = RandomNumber(r, 1, 10);
+  }
+  INVARIANT(!g_disable_xpartition_txn || allLocal);
+  if (!allLocal)
+    ++evt_tpcc_cross_partition_new_order_txns;
+
+  // XXX(stephentu): implement rollback
+  //
+  // worst case txn profile:
+  //   1 customer get
+  //   1 warehouse get
+  //   1 district get
+  //   1 new_order insert
+  //   1 district put
+  //   1 oorder insert
+  //   1 oorder_cid_idx insert
+  //   15 times:
+  //      1 item get
+  //      1 stock get
+  //      1 stock put
+  //      1 order_line insert
+  //
+  // output from txn counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 0
+  //   max_read_set_size : 15
+  //   max_write_set_size : 15
+  //   num_txn_contexts : 9
+  void *txn = db->new_txn(txn_flags, arena, txn_buf(), abstract_db::HINT_TPCC_NEW_ORDER);
+  scoped_str_arena s_arena(arena);
+  scoped_multilock<spinlock> mlock;
+  if (g_enable_partition_locks) {
+    if (allLocal) {
+      mlock.enq(LockForPartition(warehouse_id));
+    } else {
+      small_unordered_map<unsigned int, bool, 64> lockset;
+      mlock.enq(LockForPartition(warehouse_id));
+      lockset[PartitionId(warehouse_id)] = 1;
+      for (uint i = 0; i < numItems; i++) {
+        if (lockset.find(PartitionId(supplierWarehouseIDs[i])) == lockset.end()) {
+          mlock.enq(LockForPartition(supplierWarehouseIDs[i]));
+          lockset[PartitionId(supplierWarehouseIDs[i])] = 1;
+        }
+      }
+    }
+    mlock.multilock();
+  }
+  try {
+    ssize_t ret = 0;
+    const customer::key k_c(warehouse_id, districtID, customerID);
+    ALWAYS_ASSERT(tbl_customer(warehouse_id)->get(txn, Encode(obj_key0, k_c), obj_v));
+    customer::value v_c_temp;
+    const customer::value *v_c = Decode(obj_v, v_c_temp);
+    checker::SanityCheckCustomer(&k_c, v_c);
+
+    const warehouse::key k_w(warehouse_id);
+    ALWAYS_ASSERT(tbl_warehouse(warehouse_id)->get(txn, Encode(obj_key0, k_w), obj_v));
+    warehouse::value v_w_temp;
+    const warehouse::value *v_w = Decode(obj_v, v_w_temp);
+    checker::SanityCheckWarehouse(&k_w, v_w);
+
+    const district::key k_d(warehouse_id, districtID);
+    ALWAYS_ASSERT(tbl_district(warehouse_id)->get(txn, Encode(obj_key0, k_d), obj_v));
+    district::value v_d_temp;
+    const district::value *v_d = Decode(obj_v, v_d_temp);
+    checker::SanityCheckDistrict(&k_d, v_d);
+
+    const uint64_t my_next_o_id = g_new_order_fast_id_gen ?
+        FastNewOrderIdGen(warehouse_id, districtID) : v_d->d_next_o_id;
+
+    const new_order::key k_no(warehouse_id, districtID, my_next_o_id);
+    const new_order::value v_no;
+    const size_t new_order_sz = Size(v_no);
+    tbl_new_order(warehouse_id)->insert(txn, Encode(str(), k_no), Encode(str(), v_no));
+    ret += new_order_sz;
+
+    if (!g_new_order_fast_id_gen) {
+      district::value v_d_new(*v_d);
+      v_d_new.d_next_o_id++;
+      tbl_district(warehouse_id)->put(txn, Encode(str(), k_d), Encode(str(), v_d_new));
+    }
+
+    const oorder::key k_oo(warehouse_id, districtID, k_no.no_o_id);
+    oorder::value v_oo;
+    v_oo.o_c_id = int32_t(customerID);
+    v_oo.o_carrier_id = 0; // seems to be ignored
+    v_oo.o_ol_cnt = int8_t(numItems);
+    v_oo.o_all_local = allLocal;
+    v_oo.o_entry_d = GetCurrentTimeMillis();
+
+    const size_t oorder_sz = Size(v_oo);
+    tbl_oorder(warehouse_id)->insert(txn, Encode(str(), k_oo), Encode(str(), v_oo));
+    ret += oorder_sz;
+
+    const oorder_c_id_idx::key k_oo_idx(warehouse_id, districtID, customerID, k_no.no_o_id);
+    const oorder_c_id_idx::value v_oo_idx(0);
+
+    tbl_oorder_c_id_idx(warehouse_id)->insert(txn, Encode(str(), k_oo_idx), Encode(str(), v_oo_idx));
+
+    for (uint ol_number = 1; ol_number <= numItems; ol_number++) {
+      const uint ol_supply_w_id = supplierWarehouseIDs[ol_number - 1];
+      const uint ol_i_id = itemIDs[ol_number - 1];
+      const uint ol_quantity = orderQuantities[ol_number - 1];
+
+      const item::key k_i(ol_i_id);
+      ALWAYS_ASSERT(tbl_item(1)->get(txn, Encode(obj_key0, k_i), obj_v));
+      item::value v_i_temp;
+      const item::value *v_i = Decode(obj_v, v_i_temp);
+      checker::SanityCheckItem(&k_i, v_i);
+
+      const stock::key k_s(ol_supply_w_id, ol_i_id);
+      ALWAYS_ASSERT(tbl_stock(ol_supply_w_id)->get(txn, Encode(obj_key0, k_s), obj_v));
+      stock::value v_s_temp;
+      const stock::value *v_s = Decode(obj_v, v_s_temp);
+      checker::SanityCheckStock(&k_s, v_s);
+
+      stock::value v_s_new(*v_s);
+      if (v_s_new.s_quantity - ol_quantity >= 10)
+        v_s_new.s_quantity -= ol_quantity;
+      else
+        v_s_new.s_quantity += -int32_t(ol_quantity) + 91;
+      v_s_new.s_ytd += ol_quantity;
+      v_s_new.s_remote_cnt += (ol_supply_w_id == warehouse_id) ? 0 : 1;
+
+      tbl_stock(ol_supply_w_id)->put(txn, Encode(str(), k_s), Encode(str(), v_s_new));
+
+      const order_line::key k_ol(warehouse_id, districtID, k_no.no_o_id, ol_number);
+      order_line::value v_ol;
+      v_ol.ol_i_id = int32_t(ol_i_id);
+      v_ol.ol_delivery_d = 0; // not delivered yet
+      v_ol.ol_amount = float(ol_quantity) * v_i->i_price;
+      v_ol.ol_supply_w_id = int32_t(ol_supply_w_id);
+      v_ol.ol_quantity = int8_t(ol_quantity);
+
+      const size_t order_line_sz = Size(v_ol);
+      tbl_order_line(warehouse_id)->insert(txn, Encode(str(), k_ol), Encode(str(), v_ol));
+      ret += order_line_sz;
+    }
+
+    measure_txn_counters(txn, "txn_new_order");
+    if (likely(db->commit_txn(txn)))
+      return txn_result(true, ret);
+  } catch (abstract_db::abstract_abort_exception &ex) {
+    db->abort_txn(txn);
+  }
+  return txn_result(false, 0);
+}
+
+class new_order_scan_callback : public abstract_ordered_index::scan_callback {
+public:
+  new_order_scan_callback() : k_no(0) {}
+  virtual bool invoke(
+      const char *keyp, size_t keylen,
+      const string &value)
+  {
+    INVARIANT(keylen == sizeof(new_order::key));
+    INVARIANT(value.size() == sizeof(new_order::value));
+    k_no = Decode(keyp, k_no_temp);
+#ifdef CHECK_INVARIANTS
+    new_order::value v_no_temp;
+    const new_order::value *v_no = Decode(value, v_no_temp);
+    checker::SanityCheckNewOrder(k_no, v_no);
+#endif
+    return false;
+  }
+  inline const new_order::key *
+  get_key() const
+  {
+    return k_no;
+  }
+private:
+  new_order::key k_no_temp;
+  const new_order::key *k_no;
+};
+
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, delivery_probe0_tod, delivery_probe0_cg)
+
+tpcc_worker::txn_result
+tpcc_worker::txn_delivery()
+{
+  const uint warehouse_id = PickWarehouseId(r, warehouse_id_start, warehouse_id_end);
+  const uint o_carrier_id = RandomNumber(r, 1, NumDistrictsPerWarehouse());
+  const uint32_t ts = GetCurrentTimeMillis();
+
+  // worst case txn profile:
+  //   10 times:
+  //     1 new_order scan node
+  //     1 oorder get
+  //     2 order_line scan nodes
+  //     15 order_line puts
+  //     1 new_order remove
+  //     1 oorder put
+  //     1 customer get
+  //     1 customer put
+  //
+  // output from counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 21
+  //   max_read_set_size : 133
+  //   max_write_set_size : 133
+  //   num_txn_contexts : 4
+  void *txn = db->new_txn(txn_flags, arena, txn_buf(), abstract_db::HINT_TPCC_DELIVERY);
+  scoped_str_arena s_arena(arena);
+  scoped_lock_guard<spinlock> slock(
+      g_enable_partition_locks ? &LockForPartition(warehouse_id) : nullptr);
+  try {
+    ssize_t ret = 0;
+    for (uint d = 1; d <= NumDistrictsPerWarehouse(); d++) {
+      const new_order::key k_no_0(warehouse_id, d, last_no_o_ids[d - 1]);
+      const new_order::key k_no_1(warehouse_id, d, numeric_limits<int32_t>::max());
+      new_order_scan_callback new_order_c;
+      {
+        ANON_REGION("DeliverNewOrderScan:", &delivery_probe0_cg);
+        tbl_new_order(warehouse_id)->scan(txn, Encode(obj_key0, k_no_0), &Encode(obj_key1, k_no_1), new_order_c, s_arena.get());
+      }
+
+      const new_order::key *k_no = new_order_c.get_key();
+      if (unlikely(!k_no))
+        continue;
+      last_no_o_ids[d - 1] = k_no->no_o_id + 1; // XXX: update last seen
+
+      const oorder::key k_oo(warehouse_id, d, k_no->no_o_id);
+      if (unlikely(!tbl_oorder(warehouse_id)->get(txn, Encode(obj_key0, k_oo), obj_v))) {
+        // even if we read the new order entry, there's no guarantee
+        // we will read the oorder entry: in this case the txn will abort,
+        // but we're simply bailing out early
+        db->abort_txn(txn);
+        return txn_result(false, 0);
+      }
+      oorder::value v_oo_temp;
+      const oorder::value *v_oo = Decode(obj_v, v_oo_temp);
+      checker::SanityCheckOOrder(&k_oo, v_oo);
+
+      static_limit_callback<15> c(s_arena.get(), false); // never more than 15 order_lines per order
+      const order_line::key k_oo_0(warehouse_id, d, k_no->no_o_id, 0);
+      const order_line::key k_oo_1(warehouse_id, d, k_no->no_o_id, numeric_limits<int32_t>::max());
+
+      // XXX(stephentu): mutable scans would help here
+      tbl_order_line(warehouse_id)->scan(txn, Encode(obj_key0, k_oo_0), &Encode(obj_key1, k_oo_1), c, s_arena.get());
+      float sum = 0.0;
+      for (size_t i = 0; i < c.size(); i++) {
+        order_line::value v_ol_temp;
+        const order_line::value *v_ol = Decode(*c.values[i].second, v_ol_temp);
+
+#ifdef CHECK_INVARIANTS
+        order_line::key k_ol_temp;
+        const order_line::key *k_ol = Decode(*c.values[i].first, k_ol_temp);
+        checker::SanityCheckOrderLine(k_ol, v_ol);
+#endif
+
+        sum += v_ol->ol_amount;
+        order_line::value v_ol_new(*v_ol);
+        v_ol_new.ol_delivery_d = ts;
+        INVARIANT(s_arena.get()->manages(c.values[i].first));
+        tbl_order_line(warehouse_id)->put(txn, *c.values[i].first, Encode(str(), v_ol_new));
+      }
+
+      // delete new order
+      tbl_new_order(warehouse_id)->remove(txn, Encode(str(), *k_no));
+      ret -= 0 /*new_order_c.get_value_size()*/;
+
+      // update oorder
+      oorder::value v_oo_new(*v_oo);
+      v_oo_new.o_carrier_id = o_carrier_id;
+      tbl_oorder(warehouse_id)->put(txn, Encode(str(), k_oo), Encode(str(), v_oo_new));
+
+      const uint c_id = v_oo->o_c_id;
+      const float ol_total = sum;
+
+      // update customer
+      const customer::key k_c(warehouse_id, d, c_id);
+      ALWAYS_ASSERT(tbl_customer(warehouse_id)->get(txn, Encode(obj_key0, k_c), obj_v));
+
+      customer::value v_c_temp;
+      const customer::value *v_c = Decode(obj_v, v_c_temp);
+      customer::value v_c_new(*v_c);
+      v_c_new.c_balance += ol_total;
+      tbl_customer(warehouse_id)->put(txn, Encode(str(), k_c), Encode(str(), v_c_new));
+    }
+    measure_txn_counters(txn, "txn_delivery");
+    if (likely(db->commit_txn(txn)))
+      return txn_result(true, ret);
+  } catch (abstract_db::abstract_abort_exception &ex) {
+    db->abort_txn(txn);
+  }
+  return txn_result(false, 0);
+}
+
+static event_avg_counter evt_avg_cust_name_idx_scan_size("avg_cust_name_idx_scan_size");
+
+tpcc_worker::txn_result
+tpcc_worker::txn_payment()
+{
+  const uint warehouse_id = PickWarehouseId(r, warehouse_id_start, warehouse_id_end);
+  const uint districtID = RandomNumber(r, 1, NumDistrictsPerWarehouse());
+  uint customerDistrictID, customerWarehouseID;
+  if (likely(g_disable_xpartition_txn ||
+             NumWarehouses() == 1 ||
+             RandomNumber(r, 1, 100) <= 85)) {
+    customerDistrictID = districtID;
+    customerWarehouseID = warehouse_id;
+  } else {
+    customerDistrictID = RandomNumber(r, 1, NumDistrictsPerWarehouse());
+    do {
+      customerWarehouseID = RandomNumber(r, 1, NumWarehouses());
+    } while (customerWarehouseID == warehouse_id);
+  }
+  const float paymentAmount = (float) (RandomNumber(r, 100, 500000) / 100.0);
+  const uint32_t ts = GetCurrentTimeMillis();
+  INVARIANT(!g_disable_xpartition_txn || customerWarehouseID == warehouse_id);
+
+  // output from txn counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 10
+  //   max_read_set_size : 71
+  //   max_write_set_size : 1
+  //   num_txn_contexts : 5
+  void *txn = db->new_txn(txn_flags, arena, txn_buf(), abstract_db::HINT_TPCC_PAYMENT);
+  scoped_str_arena s_arena(arena);
+  scoped_multilock<spinlock> mlock;
+  if (g_enable_partition_locks) {
+    mlock.enq(LockForPartition(warehouse_id));
+    if (PartitionId(customerWarehouseID) != PartitionId(warehouse_id))
+      mlock.enq(LockForPartition(customerWarehouseID));
+    mlock.multilock();
+  }
+  if (customerWarehouseID != warehouse_id)
+    ++evt_tpcc_cross_partition_payment_txns;
+  try {
+    ssize_t ret = 0;
+
+    const warehouse::key k_w(warehouse_id);
+    ALWAYS_ASSERT(tbl_warehouse(warehouse_id)->get(txn, Encode(obj_key0, k_w), obj_v));
+    warehouse::value v_w_temp;
+    const warehouse::value *v_w = Decode(obj_v, v_w_temp);
+    checker::SanityCheckWarehouse(&k_w, v_w);
+
+    warehouse::value v_w_new(*v_w);
+    v_w_new.w_ytd += paymentAmount;
+    tbl_warehouse(warehouse_id)->put(txn, Encode(str(), k_w), Encode(str(), v_w_new));
+
+    const district::key k_d(warehouse_id, districtID);
+    ALWAYS_ASSERT(tbl_district(warehouse_id)->get(txn, Encode(obj_key0, k_d), obj_v));
+    district::value v_d_temp;
+    const district::value *v_d = Decode(obj_v, v_d_temp);
+    checker::SanityCheckDistrict(&k_d, v_d);
+
+    district::value v_d_new(*v_d);
+    v_d_new.d_ytd += paymentAmount;
+    tbl_district(warehouse_id)->put(txn, Encode(str(), k_d), Encode(str(), v_d_new));
+
+    customer::key k_c;
+    customer::value v_c;
+    if (RandomNumber(r, 1, 100) <= 60) {
+      // cust by name
+      uint8_t lastname_buf[CustomerLastNameMaxSize + 1];
+      static_assert(sizeof(lastname_buf) == 16, "xx");
+      NDB_MEMSET(lastname_buf, 0, sizeof(lastname_buf));
+      GetNonUniformCustomerLastNameRun(lastname_buf, r);
+
+      static const string zeros(16, 0);
+      static const string ones(16, 255);
+
+      customer_name_idx::key k_c_idx_0;
+      k_c_idx_0.c_w_id = customerWarehouseID;
+      k_c_idx_0.c_d_id = customerDistrictID;
+      k_c_idx_0.c_last.assign((const char *) lastname_buf, 16);
+      k_c_idx_0.c_first.assign(zeros);
+
+      customer_name_idx::key k_c_idx_1;
+      k_c_idx_1.c_w_id = customerWarehouseID;
+      k_c_idx_1.c_d_id = customerDistrictID;
+      k_c_idx_1.c_last.assign((const char *) lastname_buf, 16);
+      k_c_idx_1.c_first.assign(ones);
+
+      static_limit_callback<NMaxCustomerIdxScanElems> c(s_arena.get(), true); // probably a safe bet for now
+      tbl_customer_name_idx(customerWarehouseID)->scan(txn, Encode(obj_key0, k_c_idx_0), &Encode(obj_key1, k_c_idx_1), c, s_arena.get());
+      ALWAYS_ASSERT(c.size() > 0);
+      INVARIANT(c.size() < NMaxCustomerIdxScanElems); // we should detect this
+      int index = c.size() / 2;
+      if (c.size() % 2 == 0)
+        index--;
+      evt_avg_cust_name_idx_scan_size.offer(c.size());
+
+      customer_name_idx::value v_c_idx_temp;
+      const customer_name_idx::value *v_c_idx = Decode(*c.values[index].second, v_c_idx_temp);
+
+      k_c.c_w_id = customerWarehouseID;
+      k_c.c_d_id = customerDistrictID;
+      k_c.c_id = v_c_idx->c_id;
+      ALWAYS_ASSERT(tbl_customer(customerWarehouseID)->get(txn, Encode(obj_key0, k_c), obj_v));
+      Decode(obj_v, v_c);
+
+    } else {
+      // cust by ID
+      const uint customerID = GetCustomerId(r);
+      k_c.c_w_id = customerWarehouseID;
+      k_c.c_d_id = customerDistrictID;
+      k_c.c_id = customerID;
+      ALWAYS_ASSERT(tbl_customer(customerWarehouseID)->get(txn, Encode(obj_key0, k_c), obj_v));
+      Decode(obj_v, v_c);
+    }
+    checker::SanityCheckCustomer(&k_c, &v_c);
+    customer::value v_c_new(v_c);
+
+    v_c_new.c_balance -= paymentAmount;
+    v_c_new.c_ytd_payment += paymentAmount;
+    v_c_new.c_payment_cnt++;
+    if (strncmp(v_c.c_credit.data(), "BC", 2) == 0) {
+      char buf[501];
+      int n = snprintf(buf, sizeof(buf), "%d %d %d %d %d %f | %s",
+                       k_c.c_id,
+                       k_c.c_d_id,
+                       k_c.c_w_id,
+                       districtID,
+                       warehouse_id,
+                       paymentAmount,
+                       v_c.c_data.c_str());
+      v_c_new.c_data.resize_junk(
+          min(static_cast<size_t>(n), v_c_new.c_data.max_size()));
+      NDB_MEMCPY((void *) v_c_new.c_data.data(), &buf[0], v_c_new.c_data.size());
+    }
+
+    tbl_customer(customerWarehouseID)->put(txn, Encode(str(), k_c), Encode(str(), v_c_new));
+
+    const history::key k_h(k_c.c_d_id, k_c.c_w_id, k_c.c_id, districtID, warehouse_id, ts);
+    history::value v_h;
+    v_h.h_amount = paymentAmount;
+    v_h.h_data.resize_junk(v_h.h_data.max_size());
+    int n = snprintf((char *) v_h.h_data.data(), v_h.h_data.max_size() + 1,
+                     "%.10s    %.10s",
+                     v_w->w_name.c_str(),
+                     v_d->d_name.c_str());
+    v_h.h_data.resize_junk(min(static_cast<size_t>(n), v_h.h_data.max_size()));
+
+    const size_t history_sz = Size(v_h);
+    tbl_history(warehouse_id)->insert(txn, Encode(str(), k_h), Encode(str(), v_h));
+    ret += history_sz;
+
+    measure_txn_counters(txn, "txn_payment");
+    if (likely(db->commit_txn(txn)))
+      return txn_result(true, ret);
+  } catch (abstract_db::abstract_abort_exception &ex) {
+    db->abort_txn(txn);
+  }
+  return txn_result(false, 0);
+}
+
+class order_line_nop_callback : public abstract_ordered_index::scan_callback {
+public:
+  order_line_nop_callback() : n(0) {}
+  virtual bool invoke(
+      const char *keyp, size_t keylen,
+      const string &value)
+  {
+    INVARIANT(keylen == sizeof(order_line::key));
+    order_line::value v_ol_temp;
+    const order_line::value *v_ol UNUSED = Decode(value, v_ol_temp);
+#ifdef CHECK_INVARIANTS
+    order_line::key k_ol_temp;
+    const order_line::key *k_ol = Decode(keyp, k_ol_temp);
+    checker::SanityCheckOrderLine(k_ol, v_ol);
+#endif
+    ++n;
+    return true;
+  }
+  size_t n;
+};
+
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, order_status_probe0_tod, order_status_probe0_cg)
+
+tpcc_worker::txn_result
+tpcc_worker::txn_order_status()
+{
+  const uint warehouse_id = PickWarehouseId(r, warehouse_id_start, warehouse_id_end);
+  const uint districtID = RandomNumber(r, 1, NumDistrictsPerWarehouse());
+
+  // output from txn counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 13
+  //   max_read_set_size : 81
+  //   max_write_set_size : 0
+  //   num_txn_contexts : 4
+  const uint64_t read_only_mask =
+    g_disable_read_only_scans ? 0 : transaction_base::TXN_FLAG_READ_ONLY;
+  const abstract_db::TxnProfileHint hint =
+    g_disable_read_only_scans ?
+      abstract_db::HINT_TPCC_ORDER_STATUS :
+      abstract_db::HINT_TPCC_ORDER_STATUS_READ_ONLY;
+  void *txn = db->new_txn(txn_flags | read_only_mask, arena, txn_buf(), hint);
+  scoped_str_arena s_arena(arena);
+  // NB: since txn_order_status() is a RO txn, we assume that
+  // locking is un-necessary (since we can just read from some old snapshot)
+  try {
+
+    customer::key k_c;
+    customer::value v_c;
+    if (RandomNumber(r, 1, 100) <= 60) {
+      // cust by name
+      uint8_t lastname_buf[CustomerLastNameMaxSize + 1];
+      static_assert(sizeof(lastname_buf) == 16, "xx");
+      NDB_MEMSET(lastname_buf, 0, sizeof(lastname_buf));
+      GetNonUniformCustomerLastNameRun(lastname_buf, r);
+
+      static const string zeros(16, 0);
+      static const string ones(16, 255);
+
+      customer_name_idx::key k_c_idx_0;
+      k_c_idx_0.c_w_id = warehouse_id;
+      k_c_idx_0.c_d_id = districtID;
+      k_c_idx_0.c_last.assign((const char *) lastname_buf, 16);
+      k_c_idx_0.c_first.assign(zeros);
+
+      customer_name_idx::key k_c_idx_1;
+      k_c_idx_1.c_w_id = warehouse_id;
+      k_c_idx_1.c_d_id = districtID;
+      k_c_idx_1.c_last.assign((const char *) lastname_buf, 16);
+      k_c_idx_1.c_first.assign(ones);
+
+      static_limit_callback<NMaxCustomerIdxScanElems> c(s_arena.get(), true); // probably a safe bet for now
+      tbl_customer_name_idx(warehouse_id)->scan(txn, Encode(obj_key0, k_c_idx_0), &Encode(obj_key1, k_c_idx_1), c, s_arena.get());
+      ALWAYS_ASSERT(c.size() > 0);
+      INVARIANT(c.size() < NMaxCustomerIdxScanElems); // we should detect this
+      int index = c.size() / 2;
+      if (c.size() % 2 == 0)
+        index--;
+      evt_avg_cust_name_idx_scan_size.offer(c.size());
+
+      customer_name_idx::value v_c_idx_temp;
+      const customer_name_idx::value *v_c_idx = Decode(*c.values[index].second, v_c_idx_temp);
+
+      k_c.c_w_id = warehouse_id;
+      k_c.c_d_id = districtID;
+      k_c.c_id = v_c_idx->c_id;
+      ALWAYS_ASSERT(tbl_customer(warehouse_id)->get(txn, Encode(obj_key0, k_c), obj_v));
+      Decode(obj_v, v_c);
+
+    } else {
+      // cust by ID
+      const uint customerID = GetCustomerId(r);
+      k_c.c_w_id = warehouse_id;
+      k_c.c_d_id = districtID;
+      k_c.c_id = customerID;
+      ALWAYS_ASSERT(tbl_customer(warehouse_id)->get(txn, Encode(obj_key0, k_c), obj_v));
+      Decode(obj_v, v_c);
+    }
+    checker::SanityCheckCustomer(&k_c, &v_c);
+
+    string *newest_o_c_id = s_arena.get()->next();
+    if (g_order_status_scan_hack) {
+      // XXX(stephentu): HACK- we bound the # of elems returned by this scan to
+      // 15- this is because we don't have reverse scans. In an ideal system, a
+      // reverse scan would only need to read 1 btree node. We could simulate a
+      // lookup by only reading the first element- but then we would *always*
+      // read the first order by any customer.  To make this more interesting, we
+      // randomly select which elem to pick within the 1st or 2nd btree nodes.
+      // This is obviously a deviation from TPC-C, but it shouldn't make that
+      // much of a difference in terms of performance numbers (in fact we are
+      // making it worse for us)
+      latest_key_callback c_oorder(*newest_o_c_id, (r.next() % 15) + 1);
+      const oorder_c_id_idx::key k_oo_idx_0(warehouse_id, districtID, k_c.c_id, 0);
+      const oorder_c_id_idx::key k_oo_idx_1(warehouse_id, districtID, k_c.c_id, numeric_limits<int32_t>::max());
+      {
+        ANON_REGION("OrderStatusOOrderScan:", &order_status_probe0_cg);
+        tbl_oorder_c_id_idx(warehouse_id)->scan(txn, Encode(obj_key0, k_oo_idx_0), &Encode(obj_key1, k_oo_idx_1), c_oorder, s_arena.get());
+      }
+      ALWAYS_ASSERT(c_oorder.size());
+    } else {
+      latest_key_callback c_oorder(*newest_o_c_id, 1);
+      const oorder_c_id_idx::key k_oo_idx_hi(warehouse_id, districtID, k_c.c_id, numeric_limits<int32_t>::max());
+      tbl_oorder_c_id_idx(warehouse_id)->rscan(txn, Encode(obj_key0, k_oo_idx_hi), nullptr, c_oorder, s_arena.get());
+      ALWAYS_ASSERT(c_oorder.size() == 1);
+    }
+
+    oorder_c_id_idx::key k_oo_idx_temp;
+    const oorder_c_id_idx::key *k_oo_idx = Decode(*newest_o_c_id, k_oo_idx_temp);
+    const uint o_id = k_oo_idx->o_o_id;
+
+    order_line_nop_callback c_order_line;
+    const order_line::key k_ol_0(warehouse_id, districtID, o_id, 0);
+    const order_line::key k_ol_1(warehouse_id, districtID, o_id, numeric_limits<int32_t>::max());
+    tbl_order_line(warehouse_id)->scan(txn, Encode(obj_key0, k_ol_0), &Encode(obj_key1, k_ol_1), c_order_line, s_arena.get());
+    ALWAYS_ASSERT(c_order_line.n >= 5 && c_order_line.n <= 15);
+
+    measure_txn_counters(txn, "txn_order_status");
+    if (likely(db->commit_txn(txn)))
+      return txn_result(true, 0);
+  } catch (abstract_db::abstract_abort_exception &ex) {
+    db->abort_txn(txn);
+  }
+  return txn_result(false, 0);
+}
+
+class order_line_scan_callback : public abstract_ordered_index::scan_callback {
+public:
+  order_line_scan_callback() : n(0) {}
+  virtual bool invoke(
+      const char *keyp, size_t keylen,
+      const string &value)
+  {
+    INVARIANT(keylen == sizeof(order_line::key));
+    order_line::value v_ol_temp;
+    const order_line::value *v_ol = Decode(value, v_ol_temp);
+
+#ifdef CHECK_INVARIANTS
+    order_line::key k_ol_temp;
+    const order_line::key *k_ol = Decode(keyp, k_ol_temp);
+    checker::SanityCheckOrderLine(k_ol, v_ol);
+#endif
+
+    s_i_ids[v_ol->ol_i_id] = 1;
+    n++;
+    return true;
+  }
+  size_t n;
+  small_unordered_map<uint, bool, 512> s_i_ids;
+};
+
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, stock_level_probe0_tod, stock_level_probe0_cg)
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, stock_level_probe1_tod, stock_level_probe1_cg)
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, stock_level_probe2_tod, stock_level_probe2_cg)
+
+static event_avg_counter evt_avg_stock_level_loop_join_lookups("stock_level_loop_join_lookups");
+
+tpcc_worker::txn_result
+tpcc_worker::txn_stock_level()
+{
+  const uint warehouse_id = PickWarehouseId(r, warehouse_id_start, warehouse_id_end);
+  const uint threshold = RandomNumber(r, 10, 20);
+  const uint districtID = RandomNumber(r, 1, NumDistrictsPerWarehouse());
+
+  // output from txn counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 19
+  //   max_read_set_size : 241
+  //   max_write_set_size : 0
+  //   n_node_scan_large_instances : 1
+  //   n_read_set_large_instances : 2
+  //   num_txn_contexts : 3
+  const uint64_t read_only_mask =
+    g_disable_read_only_scans ? 0 : transaction_base::TXN_FLAG_READ_ONLY;
+  const abstract_db::TxnProfileHint hint =
+    g_disable_read_only_scans ?
+      abstract_db::HINT_TPCC_STOCK_LEVEL :
+      abstract_db::HINT_TPCC_STOCK_LEVEL_READ_ONLY;
+  void *txn = db->new_txn(txn_flags | read_only_mask, arena, txn_buf(), hint);
+  scoped_str_arena s_arena(arena);
+  // NB: since txn_stock_level() is a RO txn, we assume that
+  // locking is un-necessary (since we can just read from some old snapshot)
+  try {
+    const district::key k_d(warehouse_id, districtID);
+    ALWAYS_ASSERT(tbl_district(warehouse_id)->get(txn, Encode(obj_key0, k_d), obj_v));
+    district::value v_d_temp;
+    const district::value *v_d = Decode(obj_v, v_d_temp);
+    checker::SanityCheckDistrict(&k_d, v_d);
+
+    const uint64_t cur_next_o_id = g_new_order_fast_id_gen ?
+      NewOrderIdHolder(warehouse_id, districtID).load(memory_order_acquire) :
+      v_d->d_next_o_id;
+
+    // manual joins are fun!
+    order_line_scan_callback c;
+    const int32_t lower = cur_next_o_id >= 20 ? (cur_next_o_id - 20) : 0;
+    const order_line::key k_ol_0(warehouse_id, districtID, lower, 0);
+    const order_line::key k_ol_1(warehouse_id, districtID, cur_next_o_id, 0);
+    {
+      ANON_REGION("StockLevelOrderLineScan:", &stock_level_probe0_cg);
+      tbl_order_line(warehouse_id)->scan(txn, Encode(obj_key0, k_ol_0), &Encode(obj_key1, k_ol_1), c, s_arena.get());
+    }
+    {
+      small_unordered_map<uint, bool, 512> s_i_ids_distinct;
+      for (auto &p : c.s_i_ids) {
+        ANON_REGION("StockLevelLoopJoinIter:", &stock_level_probe1_cg);
+
+        const size_t nbytesread = serializer<int16_t, true>::max_nbytes();
+
+        const stock::key k_s(warehouse_id, p.first);
+        INVARIANT(p.first >= 1 && p.first <= NumItems());
+        {
+          ANON_REGION("StockLevelLoopJoinGet:", &stock_level_probe2_cg);
+          ALWAYS_ASSERT(tbl_stock(warehouse_id)->get(txn, Encode(obj_key0, k_s), obj_v, nbytesread));
+        }
+        INVARIANT(obj_v.size() <= nbytesread);
+        const uint8_t *ptr = (const uint8_t *) obj_v.data();
+        int16_t i16tmp;
+        ptr = serializer<int16_t, true>::read(ptr, &i16tmp);
+        if (i16tmp < int(threshold))
+          s_i_ids_distinct[p.first] = 1;
+      }
+      evt_avg_stock_level_loop_join_lookups.offer(c.s_i_ids.size());
+      // NB(stephentu): s_i_ids_distinct.size() is the computed result of this txn
+    }
+    measure_txn_counters(txn, "txn_stock_level");
+    if (likely(db->commit_txn(txn)))
+      return txn_result(true, 0);
+  } catch (abstract_db::abstract_abort_exception &ex) {
+    db->abort_txn(txn);
+  }
+  return txn_result(false, 0);
+}
+
+template <typename T>
+static vector<T>
+unique_filter(const vector<T> &v)
+{
+  set<T> seen;
+  vector<T> ret;
+  for (auto &e : v)
+    if (!seen.count(e)) {
+      ret.emplace_back(e);
+      seen.insert(e);
+    }
+  return ret;
+}
+
+class tpcc_bench_runner : public bench_runner {
+private:
+
+  static bool
+  IsTableReadOnly(const char *name)
+  {
+    return strcmp("item", name) == 0;
+  }
+
+  static bool
+  IsTableAppendOnly(const char *name)
+  {
+    return strcmp("history", name) == 0 ||
+           strcmp("oorder_c_id_idx", name) == 0;
+  }
+
+  static vector<abstract_ordered_index *>
+  OpenTablesForTablespace(abstract_db *db, const char *name, size_t expected_size)
+  {
+    const bool is_read_only = IsTableReadOnly(name);
+    const bool is_append_only = IsTableAppendOnly(name);
+    const string s_name(name);
+    vector<abstract_ordered_index *> ret(NumWarehouses());
+    if (g_enable_separate_tree_per_partition && !is_read_only) {
+      if (NumWarehouses() <= nthreads) {
+        for (size_t i = 0; i < NumWarehouses(); i++)
+          ret[i] = db->open_index(s_name + "_" + to_string(i), expected_size, is_append_only);
+      } else {
+        const unsigned nwhse_per_partition = NumWarehouses() / nthreads;
+        for (size_t partid = 0; partid < nthreads; partid++) {
+          const unsigned wstart = partid * nwhse_per_partition;
+          const unsigned wend   = (partid + 1 == nthreads) ?
+            NumWarehouses() : (partid + 1) * nwhse_per_partition;
+          abstract_ordered_index *idx =
+            db->open_index(s_name + "_" + to_string(partid), expected_size, is_append_only);
+          for (size_t i = wstart; i < wend; i++)
+            ret[i] = idx;
+        }
+      }
+    } else {
+      abstract_ordered_index *idx = db->open_index(s_name, expected_size, is_append_only);
+      for (size_t i = 0; i < NumWarehouses(); i++)
+        ret[i] = idx;
+    }
+    return ret;
+  }
+
+public:
+  tpcc_bench_runner(abstract_db *db)
+    : bench_runner(db)
+  {
+
+#define OPEN_TABLESPACE_X(x) \
+    partitions[#x] = OpenTablesForTablespace(db, #x, sizeof(x));
+
+    TPCC_TABLE_LIST(OPEN_TABLESPACE_X);
+
+#undef OPEN_TABLESPACE_X
+
+    for (auto &t : partitions) {
+      auto v = unique_filter(t.second);
+      for (size_t i = 0; i < v.size(); i++)
+        open_tables[t.first + "_" + to_string(i)] = v[i];
+    }
+
+    if (g_enable_partition_locks) {
+      static_assert(sizeof(aligned_padded_elem<spinlock>) == CACHELINE_SIZE, "xx");
+      void * const px = memalign(CACHELINE_SIZE, sizeof(aligned_padded_elem<spinlock>) * nthreads);
+      ALWAYS_ASSERT(px);
+      ALWAYS_ASSERT(reinterpret_cast<uintptr_t>(px) % CACHELINE_SIZE == 0);
+      g_partition_locks = reinterpret_cast<aligned_padded_elem<spinlock> *>(px);
+      for (size_t i = 0; i < nthreads; i++) {
+        new (&g_partition_locks[i]) aligned_padded_elem<spinlock>();
+        ALWAYS_ASSERT(!g_partition_locks[i].elem.is_locked());
+      }
+    }
+
+    if (g_new_order_fast_id_gen) {
+      void * const px =
+        memalign(
+            CACHELINE_SIZE,
+            sizeof(aligned_padded_elem<atomic<uint64_t>>) *
+              NumWarehouses() * NumDistrictsPerWarehouse());
+      g_district_ids = reinterpret_cast<aligned_padded_elem<atomic<uint64_t>> *>(px);
+      for (size_t i = 0; i < NumWarehouses() * NumDistrictsPerWarehouse(); i++)
+        new (&g_district_ids[i]) atomic<uint64_t>(3001);
+    }
+  }
+
+protected:
+  virtual vector<bench_loader *>
+  make_loaders()
+  {
+    vector<bench_loader *> ret;
+    ret.push_back(new tpcc_warehouse_loader(9324, db, open_tables, partitions));
+    ret.push_back(new tpcc_item_loader(235443, db, open_tables, partitions));
+    if (enable_parallel_loading) {
+      fast_random r(89785943);
+      for (uint i = 1; i <= NumWarehouses(); i++)
+        ret.push_back(new tpcc_stock_loader(r.next(), db, open_tables, partitions, i));
+    } else {
+      ret.push_back(new tpcc_stock_loader(89785943, db, open_tables, partitions, -1));
+    }
+    ret.push_back(new tpcc_district_loader(129856349, db, open_tables, partitions));
+    if (enable_parallel_loading) {
+      fast_random r(923587856425);
+      for (uint i = 1; i <= NumWarehouses(); i++)
+        ret.push_back(new tpcc_customer_loader(r.next(), db, open_tables, partitions, i));
+    } else {
+      ret.push_back(new tpcc_customer_loader(923587856425, db, open_tables, partitions, -1));
+    }
+    if (enable_parallel_loading) {
+      fast_random r(2343352);
+      for (uint i = 1; i <= NumWarehouses(); i++)
+        ret.push_back(new tpcc_order_loader(r.next(), db, open_tables, partitions, i));
+    } else {
+      ret.push_back(new tpcc_order_loader(2343352, db, open_tables, partitions, -1));
+    }
+    return ret;
+  }
+
+  virtual vector<bench_worker *>
+  make_workers()
+  {
+    const unsigned alignment = coreid::num_cpus_online();
+    const int blockstart =
+      coreid::allocate_contiguous_aligned_block(nthreads, alignment);
+    ALWAYS_ASSERT(blockstart >= 0);
+    ALWAYS_ASSERT((blockstart % alignment) == 0);
+    fast_random r(23984543);
+    vector<bench_worker *> ret;
+    if (NumWarehouses() <= nthreads) {
+      for (size_t i = 0; i < nthreads; i++)
+        ret.push_back(
+          new tpcc_worker(
+            blockstart + i,
+            r.next(), db, open_tables, partitions,
+            &barrier_a, &barrier_b,
+            (i % NumWarehouses()) + 1, (i % NumWarehouses()) + 2));
+    } else {
+      const unsigned nwhse_per_partition = NumWarehouses() / nthreads;
+      for (size_t i = 0; i < nthreads; i++) {
+        const unsigned wstart = i * nwhse_per_partition;
+        const unsigned wend   = (i + 1 == nthreads) ?
+          NumWarehouses() : (i + 1) * nwhse_per_partition;
+        ret.push_back(
+          new tpcc_worker(
+            blockstart + i,
+            r.next(), db, open_tables, partitions,
+            &barrier_a, &barrier_b, wstart+1, wend+1));
+      }
+    }
+    return ret;
+  }
+
+private:
+  map<string, vector<abstract_ordered_index *>> partitions;
+};
+
+void
+tpcc_do_test(abstract_db *db, int argc, char **argv)
+{
+  // parse options
+  optind = 1;
+  bool did_spec_remote_pct = false;
+  while (1) {
+    static struct option long_options[] =
+    {
+      {"disable-cross-partition-transactions" , no_argument       , &g_disable_xpartition_txn             , 1}   ,
+      {"disable-read-only-snapshots"          , no_argument       , &g_disable_read_only_scans            , 1}   ,
+      {"enable-partition-locks"               , no_argument       , &g_enable_partition_locks             , 1}   ,
+      {"enable-separate-tree-per-partition"   , no_argument       , &g_enable_separate_tree_per_partition , 1}   ,
+      {"new-order-remote-item-pct"            , required_argument , 0                                     , 'r'} ,
+      {"new-order-fast-id-gen"                , no_argument       , &g_new_order_fast_id_gen              , 1}   ,
+      {"uniform-item-dist"                    , no_argument       , &g_uniform_item_dist                  , 1}   ,
+      {"order-status-scan-hack"               , no_argument       , &g_order_status_scan_hack             , 1}   ,
+      {"workload-mix"                         , required_argument , 0                                     , 'w'} ,
+      {0, 0, 0, 0}
+    };
+    int option_index = 0;
+    int c = getopt_long(argc, argv, "r:", long_options, &option_index);
+    if (c == -1)
+      break;
+    switch (c) {
+    case 0:
+      if (long_options[option_index].flag != 0)
+        break;
+      abort();
+      break;
+
+    case 'r':
+      g_new_order_remote_item_pct = strtoul(optarg, NULL, 10);
+      ALWAYS_ASSERT(g_new_order_remote_item_pct >= 0 && g_new_order_remote_item_pct <= 100);
+      did_spec_remote_pct = true;
+      break;
+
+    case 'w':
+      {
+        const vector<string> toks = split(optarg, ',');
+        ALWAYS_ASSERT(toks.size() == ARRAY_NELEMS(g_txn_workload_mix));
+        unsigned s = 0;
+        for (size_t i = 0; i < toks.size(); i++) {
+          unsigned p = strtoul(toks[i].c_str(), nullptr, 10);
+          ALWAYS_ASSERT(p >= 0 && p <= 100);
+          s += p;
+          g_txn_workload_mix[i] = p;
+        }
+        ALWAYS_ASSERT(s == 100);
+      }
+      break;
+
+    case '?':
+      /* getopt_long already printed an error message. */
+      exit(1);
+
+    default:
+      abort();
+    }
+  }
+
+  if (did_spec_remote_pct && g_disable_xpartition_txn) {
+    cerr << "WARNING: --new-order-remote-item-pct given with --disable-cross-partition-transactions" << endl;
+    cerr << "  --new-order-remote-item-pct will have no effect" << endl;
+  }
+
+  if (verbose) {
+    cerr << "tpcc settings:" << endl;
+    cerr << "  cross_partition_transactions : " << !g_disable_xpartition_txn << endl;
+    cerr << "  read_only_snapshots          : " << !g_disable_read_only_scans << endl;
+    cerr << "  partition_locks              : " << g_enable_partition_locks << endl;
+    cerr << "  separate_tree_per_partition  : " << g_enable_separate_tree_per_partition << endl;
+    cerr << "  new_order_remote_item_pct    : " << g_new_order_remote_item_pct << endl;
+    cerr << "  new_order_fast_id_gen        : " << g_new_order_fast_id_gen << endl;
+    cerr << "  uniform_item_dist            : " << g_uniform_item_dist << endl;
+    cerr << "  order_status_scan_hack       : " << g_order_status_scan_hack << endl;
+    cerr << "  workload_mix                 : " <<
+      format_list(g_txn_workload_mix,
+                  g_txn_workload_mix + ARRAY_NELEMS(g_txn_workload_mix)) << endl;
+  }
+
+  tpcc_bench_runner r(db);
+  r.run();
+}
diff --git a/silo/benchmarks/tpcc.h b/silo/benchmarks/tpcc.h
new file mode 100644 (file)
index 0000000..ccd8a6a
--- /dev/null
@@ -0,0 +1,162 @@
+#ifndef _NDB_BENCH_TPCC_H_
+#define _NDB_BENCH_TPCC_H_
+
+#include "../record/encoder.h"
+#include "../record/inline_str.h"
+#include "../macros.h"
+
+#define CUSTOMER_KEY_FIELDS(x, y) \
+  x(int32_t,c_w_id) \
+  y(int32_t,c_d_id) \
+  y(int32_t,c_id)
+#define CUSTOMER_VALUE_FIELDS(x, y) \
+  x(float,c_discount) \
+  y(inline_str_fixed<2>,c_credit) \
+  y(inline_str_8<16>,c_last) \
+  y(inline_str_8<16>,c_first) \
+  y(float,c_credit_lim) \
+  y(float,c_balance) \
+  y(float,c_ytd_payment) \
+  y(int32_t,c_payment_cnt) \
+  y(int32_t,c_delivery_cnt) \
+  y(inline_str_8<20>,c_street_1) \
+  y(inline_str_8<20>,c_street_2) \
+  y(inline_str_8<20>,c_city) \
+  y(inline_str_fixed<2>,c_state) \
+  y(inline_str_fixed<9>,c_zip) \
+  y(inline_str_fixed<16>,c_phone) \
+  y(uint32_t,c_since) \
+  y(inline_str_fixed<2>,c_middle) \
+  y(inline_str_16<500>,c_data)
+DO_STRUCT(customer, CUSTOMER_KEY_FIELDS, CUSTOMER_VALUE_FIELDS)
+
+#define CUSTOMER_NAME_IDX_KEY_FIELDS(x, y) \
+  x(int32_t,c_w_id) \
+  y(int32_t,c_d_id) \
+  y(inline_str_fixed<16>,c_last) \
+  y(inline_str_fixed<16>,c_first)
+#define CUSTOMER_NAME_IDX_VALUE_FIELDS(x, y) \
+       x(int32_t,c_id)
+DO_STRUCT(customer_name_idx, CUSTOMER_NAME_IDX_KEY_FIELDS, CUSTOMER_NAME_IDX_VALUE_FIELDS)
+
+#define DISTRICT_KEY_FIELDS(x, y) \
+  x(int32_t,d_w_id) \
+  y(int32_t,d_id)
+#define DISTRICT_VALUE_FIELDS(x, y) \
+  x(float,d_ytd) \
+  y(float,d_tax) \
+  y(int32_t,d_next_o_id) \
+  y(inline_str_8<10>,d_name) \
+  y(inline_str_8<20>,d_street_1) \
+  y(inline_str_8<20>,d_street_2) \
+  y(inline_str_8<20>,d_city) \
+  y(inline_str_fixed<2>,d_state) \
+  y(inline_str_fixed<9>,d_zip)
+DO_STRUCT(district, DISTRICT_KEY_FIELDS, DISTRICT_VALUE_FIELDS)
+
+#define HISTORY_KEY_FIELDS(x, y) \
+  x(int32_t,h_c_id) \
+  y(int32_t,h_c_d_id) \
+  y(int32_t,h_c_w_id) \
+  y(int32_t,h_d_id) \
+  y(int32_t,h_w_id) \
+  y(uint32_t,h_date)
+#define HISTORY_VALUE_FIELDS(x, y) \
+  x(float,h_amount) \
+  y(inline_str_8<24>,h_data)
+DO_STRUCT(history, HISTORY_KEY_FIELDS, HISTORY_VALUE_FIELDS)
+
+#define ITEM_KEY_FIELDS(x, y) \
+  x(int32_t,i_id)
+#define ITEM_VALUE_FIELDS(x, y) \
+  x(inline_str_8<24>,i_name) \
+  y(float,i_price) \
+  y(inline_str_8<50>,i_data) \
+  y(int32_t,i_im_id)
+DO_STRUCT(item, ITEM_KEY_FIELDS, ITEM_VALUE_FIELDS)
+
+#define NEW_ORDER_KEY_FIELDS(x, y) \
+  x(int32_t,no_w_id) \
+  y(int32_t,no_d_id) \
+  y(int32_t,no_o_id)
+// need dummy b/c our btree cannot have empty values.
+// we also size value so that it can fit a key
+#define NEW_ORDER_VALUE_FIELDS(x, y) \
+  x(inline_str_fixed<12>,no_dummy)
+DO_STRUCT(new_order, NEW_ORDER_KEY_FIELDS, NEW_ORDER_VALUE_FIELDS)
+
+#define OORDER_KEY_FIELDS(x, y) \
+  x(int32_t,o_w_id) \
+  y(int32_t,o_d_id) \
+  y(int32_t,o_id)
+#define OORDER_VALUE_FIELDS(x, y) \
+  x(int32_t,o_c_id) \
+  y(int32_t,o_carrier_id) \
+  y(int8_t,o_ol_cnt) \
+  y(bool,o_all_local) \
+  y(uint32_t,o_entry_d)
+DO_STRUCT(oorder, OORDER_KEY_FIELDS, OORDER_VALUE_FIELDS)
+
+#define OORDER_C_ID_IDX_KEY_FIELDS(x, y) \
+  x(int32_t,o_w_id) \
+  y(int32_t,o_d_id) \
+  y(int32_t,o_c_id) \
+  y(int32_t,o_o_id)
+#define OORDER_C_ID_IDX_VALUE_FIELDS(x, y) \
+       x(uint8_t,o_dummy)
+DO_STRUCT(oorder_c_id_idx, OORDER_C_ID_IDX_KEY_FIELDS, OORDER_C_ID_IDX_VALUE_FIELDS)
+
+#define ORDER_LINE_KEY_FIELDS(x, y) \
+  x(int32_t,ol_w_id) \
+  y(int32_t,ol_d_id) \
+  y(int32_t,ol_o_id) \
+  y(int32_t,ol_number)
+#define ORDER_LINE_VALUE_FIELDS(x, y) \
+  x(int32_t,ol_i_id) \
+  y(uint32_t,ol_delivery_d) \
+  y(float,ol_amount) \
+  y(int32_t,ol_supply_w_id) \
+  y(int8_t,ol_quantity)
+DO_STRUCT(order_line, ORDER_LINE_KEY_FIELDS, ORDER_LINE_VALUE_FIELDS)
+
+#define STOCK_KEY_FIELDS(x, y) \
+  x(int32_t,s_w_id) \
+  y(int32_t,s_i_id)
+#define STOCK_VALUE_FIELDS(x, y) \
+  x(int16_t,s_quantity) \
+  y(float,s_ytd) \
+  y(int32_t,s_order_cnt) \
+  y(int32_t,s_remote_cnt)
+DO_STRUCT(stock, STOCK_KEY_FIELDS, STOCK_VALUE_FIELDS)
+
+#define STOCK_DATA_KEY_FIELDS(x, y) \
+  x(int32_t,s_w_id) \
+  y(int32_t,s_i_id)
+#define STOCK_DATA_VALUE_FIELDS(x, y) \
+  x(inline_str_8<50>,s_data) \
+  y(inline_str_fixed<24>,s_dist_01) \
+  y(inline_str_fixed<24>,s_dist_02) \
+  y(inline_str_fixed<24>,s_dist_03) \
+  y(inline_str_fixed<24>,s_dist_04) \
+  y(inline_str_fixed<24>,s_dist_05) \
+  y(inline_str_fixed<24>,s_dist_06) \
+  y(inline_str_fixed<24>,s_dist_07) \
+  y(inline_str_fixed<24>,s_dist_08) \
+  y(inline_str_fixed<24>,s_dist_09) \
+  y(inline_str_fixed<24>,s_dist_10)
+DO_STRUCT(stock_data, STOCK_DATA_KEY_FIELDS, STOCK_DATA_VALUE_FIELDS)
+
+#define WAREHOUSE_KEY_FIELDS(x, y) \
+  x(int32_t,w_id)
+#define WAREHOUSE_VALUE_FIELDS(x, y) \
+  x(float,w_ytd) \
+  y(float,w_tax) \
+  y(inline_str_8<10>,w_name) \
+  y(inline_str_8<20>,w_street_1) \
+  y(inline_str_8<20>,w_street_2) \
+  y(inline_str_8<20>,w_city) \
+  y(inline_str_fixed<2>,w_state) \
+  y(inline_str_fixed<9>,w_zip)
+DO_STRUCT(warehouse, WAREHOUSE_KEY_FIELDS, WAREHOUSE_VALUE_FIELDS)
+
+#endif
diff --git a/silo/benchmarks/ycsb.cc b/silo/benchmarks/ycsb.cc
new file mode 100644 (file)
index 0000000..80313e1
--- /dev/null
@@ -0,0 +1,519 @@
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <utility>
+#include <string>
+#include <set>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <numa.h>
+
+#include "../macros.h"
+#include "../varkey.h"
+#include "../thread.h"
+#include "../util.h"
+#include "../spinbarrier.h"
+#include "../core.h"
+
+#include "bench.h"
+
+using namespace std;
+using namespace util;
+
+static size_t nkeys;
+static const size_t YCSBRecordSize = 100;
+
+// [R, W, RMW, Scan]
+// we're missing remove for now
+// the default is a modification of YCSB "A" we made (80/20 R/W)
+static unsigned g_txn_workload_mix[] = { 80, 20, 0, 0 };
+
+class ycsb_worker : public bench_worker {
+public:
+  ycsb_worker(unsigned int worker_id,
+              unsigned long seed, abstract_db *db,
+              const map<string, abstract_ordered_index *> &open_tables,
+              spin_barrier *barrier_a, spin_barrier *barrier_b)
+    : bench_worker(worker_id, true, seed, db,
+                   open_tables, barrier_a, barrier_b),
+      tbl(open_tables.at("USERTABLE")),
+      computation_n(0)
+  {
+    obj_key0.reserve(str_arena::MinStrReserveLength);
+    obj_key1.reserve(str_arena::MinStrReserveLength);
+    obj_v.reserve(str_arena::MinStrReserveLength);
+  }
+
+  txn_result
+  txn_read()
+  {
+    void * const txn = db->new_txn(txn_flags, arena, txn_buf(), abstract_db::HINT_KV_GET_PUT);
+    scoped_str_arena s_arena(arena);
+    try {
+      const uint64_t k = r.next() % nkeys;
+      ALWAYS_ASSERT(tbl->get(txn, u64_varkey(k).str(obj_key0), obj_v));
+      computation_n += obj_v.size();
+      measure_txn_counters(txn, "txn_read");
+      if (likely(db->commit_txn(txn)))
+        return txn_result(true, 0);
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnRead(bench_worker *w)
+  {
+    return static_cast<ycsb_worker *>(w)->txn_read();
+  }
+
+  txn_result
+  txn_write()
+  {
+    void * const txn = db->new_txn(txn_flags, arena, txn_buf(), abstract_db::HINT_KV_GET_PUT);
+    scoped_str_arena s_arena(arena);
+    try {
+      tbl->put(txn, u64_varkey(r.next() % nkeys).str(str()), str().assign(YCSBRecordSize, 'b'));
+      measure_txn_counters(txn, "txn_write");
+      if (likely(db->commit_txn(txn)))
+        return txn_result(true, 0);
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnWrite(bench_worker *w)
+  {
+    return static_cast<ycsb_worker *>(w)->txn_write();
+  }
+
+  txn_result
+  txn_rmw()
+  {
+    void * const txn = db->new_txn(txn_flags, arena, txn_buf(), abstract_db::HINT_KV_RMW);
+    scoped_str_arena s_arena(arena);
+    try {
+      const uint64_t key = r.next() % nkeys;
+      ALWAYS_ASSERT(tbl->get(txn, u64_varkey(key).str(obj_key0), obj_v));
+      computation_n += obj_v.size();
+      tbl->put(txn, obj_key0, str().assign(YCSBRecordSize, 'c'));
+      measure_txn_counters(txn, "txn_rmw");
+      if (likely(db->commit_txn(txn)))
+        return txn_result(true, 0);
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnRmw(bench_worker *w)
+  {
+    return static_cast<ycsb_worker *>(w)->txn_rmw();
+  }
+
+  class worker_scan_callback : public abstract_ordered_index::scan_callback {
+  public:
+    worker_scan_callback() : n(0) {}
+    virtual bool
+    invoke(const char *, size_t, const string &value)
+    {
+      n += value.size();
+      return true;
+    }
+    size_t n;
+  };
+
+  txn_result
+  txn_scan()
+  {
+    void * const txn = db->new_txn(txn_flags, arena, txn_buf(), abstract_db::HINT_KV_SCAN);
+    scoped_str_arena s_arena(arena);
+    const size_t kstart = r.next() % nkeys;
+    const string &kbegin = u64_varkey(kstart).str(obj_key0);
+    const string &kend = u64_varkey(kstart + 100).str(obj_key1);
+    worker_scan_callback c;
+    try {
+      tbl->scan(txn, kbegin, &kend, c);
+      computation_n += c.n;
+      measure_txn_counters(txn, "txn_scan");
+      if (likely(db->commit_txn(txn)))
+        return txn_result(true, 0);
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+    return txn_result(false, 0);
+  }
+
+  static txn_result
+  TxnScan(bench_worker *w)
+  {
+    return static_cast<ycsb_worker *>(w)->txn_scan();
+  }
+
+  virtual workload_desc_vec
+  get_workload() const
+  {
+    //w.push_back(workload_desc("Read", 0.95, TxnRead));
+    //w.push_back(workload_desc("ReadModifyWrite", 0.04, TxnRmw));
+    //w.push_back(workload_desc("Write", 0.01, TxnWrite));
+
+    //w.push_back(workload_desc("Read", 1.0, TxnRead));
+    //w.push_back(workload_desc("Write", 1.0, TxnWrite));
+
+    // YCSB workload "A" - 50/50 read/write
+    //w.push_back(workload_desc("Read", 0.5, TxnRead));
+    //w.push_back(workload_desc("Write", 0.5, TxnWrite));
+
+    // YCSB workload custom - 80/20 read/write
+    //w.push_back(workload_desc("Read",  0.8, TxnRead));
+    //w.push_back(workload_desc("Write", 0.2, TxnWrite));
+
+    workload_desc_vec w;
+    unsigned m = 0;
+    for (size_t i = 0; i < ARRAY_NELEMS(g_txn_workload_mix); i++)
+      m += g_txn_workload_mix[i];
+    ALWAYS_ASSERT(m == 100);
+    if (g_txn_workload_mix[0])
+      w.push_back(workload_desc("Read",  double(g_txn_workload_mix[0])/100.0, TxnRead));
+    if (g_txn_workload_mix[1])
+      w.push_back(workload_desc("Write",  double(g_txn_workload_mix[1])/100.0, TxnWrite));
+    if (g_txn_workload_mix[2])
+      w.push_back(workload_desc("ReadModifyWrite",  double(g_txn_workload_mix[2])/100.0, TxnRmw));
+    if (g_txn_workload_mix[3])
+      w.push_back(workload_desc("Scan",  double(g_txn_workload_mix[3])/100.0, TxnScan));
+    return w;
+  }
+
+protected:
+
+  virtual void
+  on_run_setup() OVERRIDE
+  {
+    if (!pin_cpus)
+      return;
+    const size_t a = worker_id % coreid::num_cpus_online();
+    const size_t b = a % nthreads;
+    rcu::s_instance.pin_current_thread(b);
+  }
+
+  inline ALWAYS_INLINE string &
+  str() {
+    return *arena.next();
+  }
+
+private:
+  abstract_ordered_index *tbl;
+
+  string obj_key0;
+  string obj_key1;
+  string obj_v;
+
+  uint64_t computation_n;
+};
+
+static void
+ycsb_load_keyrange(
+    uint64_t keystart,
+    uint64_t keyend,
+    unsigned int pinid,
+    abstract_db *db,
+    abstract_ordered_index *tbl,
+    str_arena &arena,
+    uint64_t txn_flags,
+    void *txn_buf)
+{
+  if (pin_cpus) {
+    ALWAYS_ASSERT(pinid < nthreads);
+    rcu::s_instance.pin_current_thread(pinid);
+    rcu::s_instance.fault_region();
+  }
+
+  const size_t batchsize = (db->txn_max_batch_size() == -1) ?
+    10000 : db->txn_max_batch_size();
+  ALWAYS_ASSERT(batchsize > 0);
+  const size_t nkeys = keyend - keystart;
+  ALWAYS_ASSERT(nkeys > 0);
+  const size_t nbatches = nkeys < batchsize ? 1 : (nkeys / batchsize);
+  for (size_t batchid = 0; batchid < nbatches;) {
+    scoped_str_arena s_arena(arena);
+    void * const txn = db->new_txn(txn_flags, arena, txn_buf);
+    try {
+      const size_t rend = (batchid + 1 == nbatches) ?
+        keyend : keystart + ((batchid + 1) * batchsize);
+      for (size_t i = batchid * batchsize + keystart; i < rend; i++) {
+        ALWAYS_ASSERT(i >= keystart && i < keyend);
+        const string k = u64_varkey(i).str();
+        const string v(YCSBRecordSize, 'a');
+        tbl->insert(txn, k, v);
+      }
+      if (db->commit_txn(txn))
+        batchid++;
+      else
+        db->abort_txn(txn);
+    } catch (abstract_db::abstract_abort_exception &ex) {
+      db->abort_txn(txn);
+    }
+  }
+  if (verbose)
+    cerr << "[INFO] finished loading USERTABLE range [kstart="
+      << keystart << ", kend=" << keyend << ") - nkeys: " << nkeys << endl;
+}
+
+class ycsb_usertable_loader : public bench_loader {
+public:
+  ycsb_usertable_loader(unsigned long seed,
+                        abstract_db *db,
+                        const map<string, abstract_ordered_index *> &open_tables)
+    : bench_loader(seed, db, open_tables)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    abstract_ordered_index *tbl = open_tables.at("USERTABLE");
+    const size_t nkeysperthd = nkeys / nthreads;
+    for (size_t i = 0; i < nthreads; i++) {
+      const size_t keystart = i * nkeysperthd;
+      const size_t keyend = min((i + 1) * nkeysperthd, nkeys);
+      ycsb_load_keyrange(
+          keystart,
+          keyend,
+          i,
+          db,
+          tbl,
+          arena,
+          txn_flags,
+          txn_buf());
+    }
+  }
+};
+
+class ycsb_parallel_usertable_loader : public bench_loader {
+public:
+  ycsb_parallel_usertable_loader(unsigned long seed,
+                                 abstract_db *db,
+                                 const map<string, abstract_ordered_index *> &open_tables,
+                                 unsigned int pinid,
+                                 uint64_t keystart,
+                                 uint64_t keyend)
+    : bench_loader(seed, db, open_tables),
+      pinid(pinid), keystart(keystart), keyend(keyend)
+  {
+    INVARIANT(keyend > keystart);
+    if (verbose)
+      cerr << "[INFO] YCSB par loader cpu " << pinid
+           << " [" << keystart << ", " << keyend << ")" << endl;
+  }
+
+protected:
+  virtual void
+  load()
+  {
+    abstract_ordered_index *tbl = open_tables.at("USERTABLE");
+    ycsb_load_keyrange(
+        keystart,
+        keyend,
+        pinid,
+        db,
+        tbl,
+        arena,
+        txn_flags,
+        txn_buf());
+  }
+
+private:
+  unsigned int pinid;
+  uint64_t keystart;
+  uint64_t keyend;
+};
+
+
+class ycsb_bench_runner : public bench_runner {
+public:
+  ycsb_bench_runner(abstract_db *db)
+    : bench_runner(db)
+  {
+    open_tables["USERTABLE"] = db->open_index("USERTABLE", YCSBRecordSize);
+  }
+
+protected:
+  virtual vector<bench_loader *>
+  make_loaders()
+  {
+    vector<bench_loader *> ret;
+    const unsigned long ncpus = coreid::num_cpus_online();
+    if (enable_parallel_loading && nkeys >= nthreads) {
+      // divide the key space amongst all the loaders
+      const size_t nkeysperloader = nkeys / ncpus;
+      if (nthreads > ncpus) {
+        for (size_t i = 0; i < ncpus; i++) {
+          const uint64_t kend = (i + 1 == ncpus) ?
+            nkeys : (i + 1) * nkeysperloader;
+          ret.push_back(
+              new ycsb_parallel_usertable_loader(
+                0, db, open_tables, i,
+                i * nkeysperloader, kend));
+        }
+      } else {
+        // load balance the loaders amongst numa nodes in RR fashion
+        //
+        // XXX: here we hardcode an assumption about the NUMA topology of
+        // the system
+        const vector<unsigned> numa_nodes_used = get_numa_nodes_used(nthreads);
+
+        // assign loaders to cores based on numa node assignment in RR fashion
+        const unsigned loaders_per_node = ncpus / numa_nodes_used.size();
+
+        vector<unsigned> node_allocations(numa_nodes_used.size(), loaders_per_node);
+        // RR the remaining
+        for (unsigned i = 0;
+             i < (ncpus - loaders_per_node * numa_nodes_used.size());
+             i++)
+          node_allocations[i]++;
+
+        size_t loader_i = 0;
+        for (size_t i = 0; i < numa_nodes_used.size(); i++) {
+          // allocate loaders_per_node loaders to this numa node
+          const vector<unsigned> cpus = numa_node_to_cpus(numa_nodes_used[i]);
+          const vector<unsigned> cpus_avail = exclude(cpus, nthreads);
+          const unsigned nloaders = node_allocations[i];
+          for (size_t j = 0; j < nloaders; j++, loader_i++) {
+            const uint64_t kend = (loader_i + 1 == ncpus) ?
+              nkeys : (loader_i + 1) * nkeysperloader;
+            ret.push_back(
+                new ycsb_parallel_usertable_loader(
+                  0, db, open_tables, cpus_avail[j % cpus_avail.size()],
+                  loader_i * nkeysperloader, kend));
+          }
+        }
+      }
+    } else {
+      ret.push_back(new ycsb_usertable_loader(0, db, open_tables));
+    }
+    return ret;
+  }
+
+  virtual vector<bench_worker *>
+  make_workers()
+  {
+    const unsigned alignment = coreid::num_cpus_online();
+    const int blockstart =
+      coreid::allocate_contiguous_aligned_block(nthreads, alignment);
+    ALWAYS_ASSERT(blockstart >= 0);
+    ALWAYS_ASSERT((blockstart % alignment) == 0);
+    fast_random r(8544290);
+    vector<bench_worker *> ret;
+    for (size_t i = 0; i < nthreads; i++)
+      ret.push_back(
+        new ycsb_worker(
+          blockstart + i, r.next(), db, open_tables,
+          &barrier_a, &barrier_b));
+    return ret;
+  }
+
+private:
+
+  static vector<unsigned>
+  get_numa_nodes_used(unsigned nthds)
+  {
+    // assuming CPUs [0, nthds) are used, what are all the
+    // NUMA nodes touched by [0, nthds)
+    set<unsigned> ret;
+    for (unsigned i = 0; i < nthds; i++) {
+      const int node = numa_node_of_cpu(i);
+      ALWAYS_ASSERT(node >= 0);
+      ret.insert(node);
+    }
+    return vector<unsigned>(ret.begin(), ret.end());
+  }
+
+  static vector<unsigned>
+  numa_node_to_cpus(unsigned node)
+  {
+    struct bitmask *bm = numa_allocate_cpumask();
+    ALWAYS_ASSERT(!::numa_node_to_cpus(node, bm));
+    vector<unsigned> ret;
+    for (int i = 0; i < numa_num_configured_cpus(); i++)
+      if (numa_bitmask_isbitset(bm, i))
+        ret.push_back(i);
+    numa_free_cpumask(bm);
+    return ret;
+  }
+
+  static vector<unsigned>
+  exclude(const vector<unsigned> &cpus, unsigned nthds)
+  {
+    vector<unsigned> ret;
+    for (auto n : cpus)
+      if (n < nthds)
+        ret.push_back(n);
+    return ret;
+  }
+
+};
+
+void
+ycsb_do_test(abstract_db *db, int argc, char **argv)
+{
+  nkeys = size_t(scale_factor * 1000.0);
+  ALWAYS_ASSERT(nkeys > 0);
+
+  // parse options
+  optind = 1;
+  while (1) {
+    static struct option long_options[] = {
+      {"workload-mix" , required_argument , 0 , 'w'},
+      {0, 0, 0, 0}
+    };
+    int option_index = 0;
+    int c = getopt_long(argc, argv, "w:", long_options, &option_index);
+    if (c == -1)
+      break;
+    switch (c) {
+    case 0:
+      if (long_options[option_index].flag != 0)
+        break;
+      abort();
+      break;
+
+    case 'w':
+      {
+        const vector<string> toks = split(optarg, ',');
+        ALWAYS_ASSERT(toks.size() == ARRAY_NELEMS(g_txn_workload_mix));
+        unsigned s = 0;
+        for (size_t i = 0; i < toks.size(); i++) {
+          unsigned p = strtoul(toks[i].c_str(), nullptr, 10);
+          ALWAYS_ASSERT(p >= 0 && p <= 100);
+          s += p;
+          g_txn_workload_mix[i] = p;
+        }
+        ALWAYS_ASSERT(s == 100);
+      }
+      break;
+
+    case '?':
+      /* getopt_long already printed an error message. */
+      exit(1);
+
+    default:
+      abort();
+    }
+  }
+
+  if (verbose) {
+    cerr << "ycsb settings:" << endl;
+    cerr << "  workload_mix: "
+         << format_list(g_txn_workload_mix, g_txn_workload_mix + ARRAY_NELEMS(g_txn_workload_mix))
+         << endl;
+  }
+
+  ycsb_bench_runner r(db);
+  r.run();
+}
diff --git a/silo/btree.cc b/silo/btree.cc
new file mode 100644 (file)
index 0000000..14dddd0
--- /dev/null
@@ -0,0 +1,2010 @@
+#include <unistd.h>
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <stack>
+#include <vector>
+#include <sstream>
+#include <atomic>
+#include <memory>
+
+#include "core.h"
+#include "btree.h"
+#include "btree_impl.h"
+#include "thread.h"
+#include "txn.h"
+#include "util.h"
+#include "scopedperf.hh"
+
+#if defined(NDB_MASSTREE)
+#include "masstree_btree.h"
+struct testing_concurrent_btree_traits : public masstree_params {
+  static const bool RcuRespCaller = false;
+};
+typedef mbtree<testing_concurrent_btree_traits> testing_concurrent_btree;
+#define HAVE_REVERSE_RANGE_SCANS
+#else
+struct testing_concurrent_btree_traits : public concurrent_btree_traits {
+  static const bool RcuRespCaller = false;
+};
+typedef btree<testing_concurrent_btree_traits> testing_concurrent_btree;
+#endif
+
+using namespace std;
+using namespace util;
+
+class scoped_rate_timer {
+private:
+  util::timer t;
+  string region;
+  size_t n;
+
+public:
+  scoped_rate_timer(const string &region, size_t n) : region(region), n(n)
+  {}
+
+  ~scoped_rate_timer()
+  {
+    double x = t.lap() / 1000.0; // ms
+    double rate = double(n) / (x / 1000.0);
+    cerr << "timed region `" << region << "' took " << x
+              << " ms (" << rate << " events/sec)" << endl;
+  }
+};
+
+class btree_worker : public ndb_thread {
+public:
+  btree_worker(testing_concurrent_btree *btr) : btr(btr)  {}
+  btree_worker(testing_concurrent_btree &btr) : btr(&btr) {}
+protected:
+  testing_concurrent_btree *const btr;
+};
+
+static void
+test1()
+{
+  testing_concurrent_btree btr;
+  btr.invariant_checker();
+
+  // fill up root leaf node
+  for (size_t i = 0; i < testing_concurrent_btree::NKeysPerNode; i++) {
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+  ALWAYS_ASSERT(btr.size() == testing_concurrent_btree::NKeysPerNode);
+
+  // induce a split
+  btr.insert(u64_varkey(testing_concurrent_btree::NKeysPerNode), (typename testing_concurrent_btree::value_type) (testing_concurrent_btree::NKeysPerNode));
+  btr.invariant_checker();
+  ALWAYS_ASSERT(btr.size() == testing_concurrent_btree::NKeysPerNode + 1);
+
+  // now make sure we can find everything post split
+  for (size_t i = 0; i < testing_concurrent_btree::NKeysPerNode + 1; i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+
+  // now fill up the new root node
+  const size_t n = (testing_concurrent_btree::NKeysPerNode + testing_concurrent_btree::NKeysPerNode * (testing_concurrent_btree::NMinKeysPerNode));
+  for (size_t i = testing_concurrent_btree::NKeysPerNode + 1; i < n; i++) {
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+  ALWAYS_ASSERT(btr.size() == n);
+
+  // cause the root node to split
+  btr.insert(u64_varkey(n), (typename testing_concurrent_btree::value_type) n);
+  btr.invariant_checker();
+  ALWAYS_ASSERT(btr.size() == n + 1);
+
+  // once again make sure we can find everything
+  for (size_t i = 0; i < n + 1; i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+}
+
+static void
+test2()
+{
+  testing_concurrent_btree btr;
+  const size_t n = 1000;
+  for (size_t i = 0; i < n; i += 2) {
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+
+  for (size_t i = 1; i < n; i += 2) {
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+
+  ALWAYS_ASSERT(btr.size() == n);
+}
+
+static void
+test3()
+{
+  testing_concurrent_btree btr;
+
+  for (size_t i = 0; i < testing_concurrent_btree::NKeysPerNode * 2; i++) {
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+  ALWAYS_ASSERT(btr.size() == testing_concurrent_btree::NKeysPerNode * 2);
+
+  for (size_t i = 0; i < testing_concurrent_btree::NKeysPerNode * 2; i++) {
+    btr.remove(u64_varkey(i));
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(u64_varkey(i), v));
+  }
+  ALWAYS_ASSERT(btr.size() == 0);
+
+  for (size_t i = 0; i < testing_concurrent_btree::NKeysPerNode * 2; i++) {
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+  ALWAYS_ASSERT(btr.size() == testing_concurrent_btree::NKeysPerNode * 2);
+
+  for (ssize_t i = testing_concurrent_btree::NKeysPerNode * 2 - 1; i >= 0; i--) {
+    btr.remove(u64_varkey(i));
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(u64_varkey(i), v));
+  }
+  ALWAYS_ASSERT(btr.size() == 0);
+
+  for (size_t i = 0; i < testing_concurrent_btree::NKeysPerNode * 2; i++) {
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+  ALWAYS_ASSERT(btr.size() == testing_concurrent_btree::NKeysPerNode * 2);
+
+  for (ssize_t i = testing_concurrent_btree::NKeysPerNode; i >= 0; i--) {
+    btr.remove(u64_varkey(i));
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(u64_varkey(i), v));
+  }
+
+  for (size_t i = testing_concurrent_btree::NKeysPerNode + 1; i < testing_concurrent_btree::NKeysPerNode * 2; i++) {
+    btr.remove(u64_varkey(i));
+    btr.invariant_checker();
+
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(u64_varkey(i), v));
+  }
+  ALWAYS_ASSERT(btr.size() == 0);
+}
+
+static void
+test4()
+{
+  testing_concurrent_btree btr;
+  const size_t nkeys = 10000;
+  for (size_t i = 0; i < nkeys; i++) {
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    btr.invariant_checker();
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+  ALWAYS_ASSERT(btr.size() == nkeys);
+
+  srand(12345);
+
+  for (size_t i = 0; i < nkeys; i++) {
+    size_t k = rand() % nkeys;
+    btr.remove(u64_varkey(k));
+    btr.invariant_checker();
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(u64_varkey(k), v));
+  }
+
+  for (size_t i = 0; i < nkeys; i++) {
+    btr.remove(u64_varkey(i));
+    btr.invariant_checker();
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(u64_varkey(i), v));
+  }
+  ALWAYS_ASSERT(btr.size() == 0);
+}
+
+static void
+test5()
+{
+  // insert in random order, delete in random order
+  testing_concurrent_btree btr;
+
+  unsigned int seeds[] = {
+    54321, 2013883780, 3028985725, 3058602342, 256561598, 2895653051
+  };
+
+  for (size_t iter = 0; iter < ARRAY_NELEMS(seeds); iter++) {
+    srand(seeds[iter]);
+    const size_t nkeys = 20000;
+    set<size_t> s;
+    for (size_t i = 0; i < nkeys; i++) {
+      size_t k = rand() % nkeys;
+      s.insert(k);
+      btr.insert(u64_varkey(k), (typename testing_concurrent_btree::value_type) k);
+      btr.invariant_checker();
+      typename testing_concurrent_btree::value_type v = 0;
+      ALWAYS_ASSERT(btr.search(u64_varkey(k), v));
+      ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) k);
+    }
+    ALWAYS_ASSERT(btr.size() == s.size());
+
+    for (size_t i = 0; i < nkeys * 2; i++) {
+      size_t k = rand() % nkeys;
+      btr.remove(u64_varkey(k));
+      btr.invariant_checker();
+      typename testing_concurrent_btree::value_type v = 0;
+      ALWAYS_ASSERT(!btr.search(u64_varkey(k), v));
+    }
+
+    // clean it up
+    for (size_t i = 0; i < nkeys; i++) {
+      btr.remove(u64_varkey(i));
+      btr.invariant_checker();
+      typename testing_concurrent_btree::value_type v = 0;
+      ALWAYS_ASSERT(!btr.search(u64_varkey(i), v));
+    }
+
+    ALWAYS_ASSERT(btr.size() == 0);
+  }
+}
+
+namespace test6_ns {
+  struct scan_callback {
+    typedef vector<
+      pair< std::string, // we want to make copies of keys
+            typename testing_concurrent_btree::value_type > > kv_vec;
+    scan_callback(kv_vec *data, bool reverse = false)
+      : data(data), reverse_(reverse) {}
+    inline bool
+    operator()(const typename testing_concurrent_btree::string_type &k,
+                     typename testing_concurrent_btree::value_type v) const
+    {
+      if (!data->empty()) {
+        const bool geq =
+          typename testing_concurrent_btree::string_type(data->back().first) >= k;
+        const bool leq =
+          typename testing_concurrent_btree::string_type(data->back().first) <= k;
+        if ((!reverse_ && geq) || (reverse_ && leq)) {
+          cerr << "data->size(): " << data->size() << endl;
+          cerr << "prev: " << varkey(data->back().first) << endl;
+          cerr << "cur : " << varkey(k) << endl;
+          ALWAYS_ASSERT(false);
+        }
+      }
+      data->push_back(make_pair(k, v));
+      return true;
+    }
+    kv_vec *data;
+    bool reverse_;
+  };
+}
+
+static void
+test6()
+{
+  testing_concurrent_btree btr;
+  const size_t nkeys = 1000;
+  for (size_t i = 0; i < nkeys; i++)
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+  btr.invariant_checker();
+  ALWAYS_ASSERT(btr.size() == nkeys);
+
+  using namespace test6_ns;
+
+  scan_callback::kv_vec data;
+  scan_callback cb(&data);
+  u64_varkey max_key(600);
+  btr.search_range(u64_varkey(500), &max_key, cb);
+  ALWAYS_ASSERT(data.size() == 100);
+  for (size_t i = 0; i < 100; i++) {
+    const varkey lhs(data[i].first), rhs(u64_varkey(500 + i));
+    ALWAYS_ASSERT(lhs == rhs);
+    ALWAYS_ASSERT(data[i].second == (typename testing_concurrent_btree::value_type) (500 + i));
+  }
+
+  data.clear();
+  btr.search_range(u64_varkey(500), NULL, cb);
+  ALWAYS_ASSERT(data.size() == 500);
+  for (size_t i = 0; i < 500; i++) {
+    ALWAYS_ASSERT(varkey(data[i].first) == u64_varkey(500 + i));
+    ALWAYS_ASSERT(data[i].second == (typename testing_concurrent_btree::value_type) (500 + i));
+  }
+
+#ifdef HAVE_REVERSE_RANGE_SCANS
+  data.clear();
+  scan_callback cb_rev(&data, true);
+  btr.rsearch_range(u64_varkey(499), NULL, cb_rev);
+  ALWAYS_ASSERT(data.size() == 500);
+  for (ssize_t i = 499; i >= 0; i--) {
+    ALWAYS_ASSERT(varkey(data[499 - i].first) == u64_varkey(i));
+    ALWAYS_ASSERT(data[499 - i].second == (typename testing_concurrent_btree::value_type) (i));
+  }
+
+  data.clear();
+  u64_varkey min_key(499);
+  btr.rsearch_range(u64_varkey(999), &min_key, cb_rev);
+  ALWAYS_ASSERT(data.size() == 500);
+  for (ssize_t i = 999; i >= 500; i--) {
+    ALWAYS_ASSERT(varkey(data[999 - i].first) == u64_varkey(i));
+    ALWAYS_ASSERT(data[999 - i].second == (typename testing_concurrent_btree::value_type) (i));
+  }
+#endif
+}
+
+static void
+test7()
+{
+  testing_concurrent_btree btr;
+  ALWAYS_ASSERT(!btr.remove(u64_varkey(0)));
+  ALWAYS_ASSERT(btr.insert(u64_varkey(0), (typename testing_concurrent_btree::value_type) 0));
+  ALWAYS_ASSERT(!btr.insert(u64_varkey(0), (typename testing_concurrent_btree::value_type) 1));
+  typename testing_concurrent_btree::value_type v;
+  ALWAYS_ASSERT(btr.search(u64_varkey(0), v));
+  ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) 1);
+  ALWAYS_ASSERT(!btr.insert_if_absent(u64_varkey(0), (typename testing_concurrent_btree::value_type) 2));
+  ALWAYS_ASSERT(btr.search(u64_varkey(0), v));
+  ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) 1);
+  ALWAYS_ASSERT(btr.remove(u64_varkey(0)));
+  ALWAYS_ASSERT(btr.insert_if_absent(u64_varkey(0), (typename testing_concurrent_btree::value_type) 2));
+  ALWAYS_ASSERT(btr.search(u64_varkey(0), v));
+  ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) 2);
+}
+
+static void
+test_varlen_single_layer()
+{
+  testing_concurrent_btree btr;
+
+  const char *k0 = "a";
+  const char *k1 = "aa";
+  const char *k2 = "aaa";
+  const char *k3 = "aaaa";
+  const char *k4 = "aaaaa";
+
+  const char *keys[] = {k0, k1, k2, k3, k4};
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    ALWAYS_ASSERT(btr.insert(varkey(keys[i]), (typename testing_concurrent_btree::value_type) keys[i]));
+    btr.invariant_checker();
+  }
+
+  ALWAYS_ASSERT(btr.size() == ARRAY_NELEMS(keys));
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(varkey(keys[i]), v));
+    ALWAYS_ASSERT(strcmp((const char *) v, keys[i]) == 0);
+  }
+
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    ALWAYS_ASSERT(btr.remove(varkey(keys[i])));
+    btr.invariant_checker();
+  }
+  ALWAYS_ASSERT(btr.size() == 0);
+}
+
+static void
+test_varlen_multi_layer()
+{
+  testing_concurrent_btree btr;
+
+  const char *k0 = "aaaaaaa";
+  const char *k1 = "aaaaaaaa";
+  const char *k2 = "aaaaaaaaa";
+  const char *k3 = "aaaaaaaaaa";
+  const char *k4 = "aaaaaaaaaaa";
+  const char *k5 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+  const char *keys[] = {k0, k1, k2, k3, k4, k5};
+
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    ALWAYS_ASSERT(btr.insert(varkey(keys[i]), (typename testing_concurrent_btree::value_type) keys[i]));
+    btr.invariant_checker();
+  }
+
+  ALWAYS_ASSERT(btr.size() == ARRAY_NELEMS(keys));
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(varkey(keys[i]), v));
+    ALWAYS_ASSERT(strcmp((const char *) v, keys[i]) == 0);
+  }
+
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    ALWAYS_ASSERT(btr.remove(varkey(keys[i])));
+    btr.invariant_checker();
+  }
+  ALWAYS_ASSERT(btr.size() == 0);
+}
+
+static void
+test_two_layer()
+{
+  const char *k0 = "aaaaaaaaa";
+  const char *k1 = "aaaaaaaaaa";
+
+  testing_concurrent_btree btr;
+  ALWAYS_ASSERT(btr.insert(varkey(k0), (typename testing_concurrent_btree::value_type) k0));
+  ALWAYS_ASSERT(btr.insert(varkey(k1), (typename testing_concurrent_btree::value_type) k1));
+  ALWAYS_ASSERT(btr.size() == 2);
+}
+
+static __attribute__((used)) void test_ensure_printable() {
+    testing_concurrent_btree btr;
+    btr.print();
+}
+
+class test_range_scan_helper : public testing_concurrent_btree::search_range_callback {
+public:
+
+  struct expect {
+    expect() : tag(), expected_size() {}
+    expect(size_t expected_size)
+      : tag(0), expected_size(expected_size) {}
+    expect(const set<string> &expected_keys)
+      : tag(1), expected_keys(expected_keys) {}
+    uint8_t tag;
+    size_t expected_size;
+    set<string> expected_keys;
+  };
+
+  enum ExpectType {
+    EXPECT_EXACT,
+    EXPECT_ATLEAST,
+  };
+
+  test_range_scan_helper(
+    testing_concurrent_btree &btr,
+    const testing_concurrent_btree::key_type &begin,
+    const testing_concurrent_btree::key_type *end,
+    bool reverse,
+    const expect &expectation,
+    ExpectType ex_type = EXPECT_EXACT)
+    : btr(&btr),
+      begin(begin),
+      end(end ? new testing_concurrent_btree::key_type(*end) : NULL),
+      reverse_(reverse),
+      expectation(expectation),
+      ex_type(ex_type)
+  {
+  }
+
+  ~test_range_scan_helper()
+  {
+    if (end)
+      delete end;
+  }
+
+  virtual bool
+  invoke(const typename testing_concurrent_btree::string_type &k,
+         typename testing_concurrent_btree::value_type v)
+  {
+    VERBOSE(cerr << "test_range_scan_helper::invoke(): received key(size="
+                 << k.size() << "): " << hexify(k) << endl);
+    if (!keys.empty()) {
+      if (!reverse_)
+        ALWAYS_ASSERT(typename testing_concurrent_btree::string_type(keys.back()) < k);
+      else
+        ALWAYS_ASSERT(typename testing_concurrent_btree::string_type(keys.back()) > k);
+    }
+    keys.push_back(k);
+    return true;
+  }
+
+  void test()
+  {
+    keys.clear();
+    if (!reverse_)
+      btr->search_range_call(begin, end, *this);
+    else
+      btr->rsearch_range_call(begin, end, *this);
+    if (expectation.tag == 0) {
+      switch (ex_type) {
+      case EXPECT_EXACT:
+        ALWAYS_ASSERT(keys.size() == expectation.expected_size);
+        break;
+      case EXPECT_ATLEAST:
+        ALWAYS_ASSERT(keys.size() >= expectation.expected_size);
+        break;
+      }
+    } else {
+      switch (ex_type) {
+      case EXPECT_EXACT: {
+        ALWAYS_ASSERT(keys.size() == expectation.expected_keys.size());
+        vector<string> cmp;
+        if (!reverse_)
+          cmp.assign(expectation.expected_keys.begin(), expectation.expected_keys.end());
+        else
+          cmp.assign(expectation.expected_keys.rbegin(), expectation.expected_keys.rend());
+        for (size_t i = 0; i < keys.size(); i++) {
+          if (keys[i] != cmp[i]) {
+            cerr << "A: " << hexify(keys[i]) << endl;
+            cerr << "B: " << hexify(cmp[i]) << endl;
+            ALWAYS_ASSERT(false);
+          }
+        }
+        break;
+      }
+      case EXPECT_ATLEAST: {
+        ALWAYS_ASSERT(keys.size() >= expectation.expected_keys.size());
+        // every key in the expected set must be present
+        set<string> keyset(keys.begin(), keys.end());
+        for (auto it = expectation.expected_keys.begin();
+             it != expectation.expected_keys.end(); ++it)
+          ALWAYS_ASSERT(keyset.count(*it) == 1);
+        break;
+      }
+      }
+    }
+  }
+
+private:
+  testing_concurrent_btree *const btr;
+  testing_concurrent_btree::key_type begin;
+  testing_concurrent_btree::key_type *end;
+  bool reverse_;
+  expect expectation;
+  ExpectType ex_type;
+
+  vector<string> keys;
+};
+
+static void
+test_two_layer_range_scan()
+{
+  const char *keys[] = {
+    "a",
+    "aaaaaaaa",
+    "aaaaaaaaa",
+    "aaaaaaaaaa",
+    "aaaaaaaaaaa",
+    "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
+    "l", "m", "n", "o", "p", "q", "r", "s",
+  };
+
+  testing_concurrent_btree btr;
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    ALWAYS_ASSERT(btr.insert(varkey(keys[i]), (typename testing_concurrent_btree::value_type) keys[i]));
+    btr.invariant_checker();
+  }
+
+  test_range_scan_helper::expect ex(set<string>(keys, keys + ARRAY_NELEMS(keys)));
+  test_range_scan_helper tester(btr, varkey(""), NULL, false, ex);
+  tester.test();
+
+#ifdef HAVE_REVERSE_RANGE_SCANS
+  test_range_scan_helper tester_rev(btr, varkey("zzzzzzzzzzzzzzzzzzzzzz"), NULL, true, ex);
+  tester_rev.test();
+#endif
+}
+
+static void
+test_multi_layer_scan()
+{
+  const uint8_t lokey_cstr[] = {
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x45, 0x49, 0x4E, 0x47,
+    0x41, 0x54, 0x49, 0x4F, 0x4E, 0x45, 0x49, 0x4E, 0x47, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00
+  };
+  const uint8_t hikey_cstr[] = {
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x45, 0x49, 0x4E, 0x47,
+    0x41, 0x54, 0x49, 0x4F, 0x4E, 0x45, 0x49, 0x4E, 0x47, 0x00, 0x00, 0x00,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF
+  };
+  const string lokey_s((const char *) &lokey_cstr[0], ARRAY_NELEMS(lokey_cstr));
+  const string hikey_s((const char *) &hikey_cstr[0], ARRAY_NELEMS(hikey_cstr));
+
+  string lokey_s_next(lokey_s);
+  lokey_s_next.resize(lokey_s_next.size() + 1);
+
+  const varkey hikey(hikey_s);
+
+  testing_concurrent_btree btr;
+  ALWAYS_ASSERT(btr.insert(varkey(lokey_s), (typename testing_concurrent_btree::value_type) 0x123));
+
+  test_range_scan_helper::expect ex(0);
+  test_range_scan_helper tester(btr, varkey(lokey_s_next), &hikey, false, ex);
+  tester.test();
+
+#ifdef HAVE_REVERSE_RANGE_SCANS
+  const varkey lokey(lokey_s);
+  test_range_scan_helper tester_rev(btr, varkey(hikey_s), &lokey, true, ex);
+  tester_rev.test();
+#endif
+}
+
+static void
+test_null_keys()
+{
+  const uint8_t k0[] = {};
+  const uint8_t k1[] = {'\0'};
+  const uint8_t k2[] = {'\0', '\0'};
+  const uint8_t k3[] = {'\0', '\0', '\0'};
+  const uint8_t k4[] = {'\0', '\0', '\0', '\0'};
+  const uint8_t k5[] = {'\0', '\0', '\0', '\0', '\0'};
+  const uint8_t k6[] = {'\0', '\0', '\0', '\0', '\0', '\0'};
+  const uint8_t k7[] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0'};
+  const uint8_t k8[] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'};
+  const uint8_t k9[] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'};
+  const uint8_t k10[] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'};
+  const uint8_t *keys[] = {k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10};
+
+  testing_concurrent_btree btr;
+
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    ALWAYS_ASSERT(btr.insert(varkey(keys[i], i), (typename testing_concurrent_btree::value_type) i));
+    btr.invariant_checker();
+  }
+
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(varkey(keys[i], i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+
+  for (size_t i = 1; i <= 20; i++) {
+    ALWAYS_ASSERT(btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i));
+    btr.invariant_checker();
+  }
+
+  for (size_t i = 0; i < ARRAY_NELEMS(keys); i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(varkey(keys[i], i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+
+  for (size_t i = 1; i <= 20; i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+}
+
+static void
+test_null_keys_2()
+{
+  const size_t nprefixes = 200;
+
+  testing_concurrent_btree btr;
+
+  fast_random r(9084398309893);
+
+  set<string> prefixes;
+  for (size_t i = 0; i < nprefixes; i++) {
+  retry:
+    const string k(r.next_string(r.next() % 30));
+    if (prefixes.count(k) == 1)
+      goto retry;
+    prefixes.insert(k);
+  }
+
+  set<string> keys;
+  for (auto &prefix : prefixes) {
+    for (size_t i = 1; i <= 12; i++) {
+      std::string x(prefix);
+      x.resize(x.size() + i);
+      keys.insert(x);
+    }
+  }
+
+  size_t ctr = 1;
+  for (auto it = keys.begin(); it != keys.end(); ++it, ++ctr) {
+    ALWAYS_ASSERT(btr.insert(varkey(*it), (typename testing_concurrent_btree::value_type) it->data()));
+    btr.invariant_checker();
+    ALWAYS_ASSERT(btr.size() == ctr);
+  }
+  ALWAYS_ASSERT(btr.size() == keys.size());
+
+  for (auto it = keys.begin(); it != keys.end(); ++it) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(varkey(*it), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) it->data());
+  }
+
+  test_range_scan_helper::expect ex(keys);
+  test_range_scan_helper tester(btr, varkey(*keys.begin()), NULL, false, ex);
+  tester.test();
+
+#ifdef HAVE_REVERSE_RANGE_SCANS
+  test_range_scan_helper tester_rev(btr, varkey(*keys.rbegin()), NULL, true, ex);
+  tester_rev.test();
+#endif
+
+  ctr = keys.size() - 1;
+  for (auto it = keys.begin(); it != keys.end(); ++it, --ctr) {
+    ALWAYS_ASSERT(btr.remove(varkey(*it)));
+    btr.invariant_checker();
+    ALWAYS_ASSERT(btr.size() == ctr);
+  }
+  ALWAYS_ASSERT(btr.size() == 0);
+}
+
+static inline string
+maxkey(unsigned size)
+{
+  return string(size, 255);
+}
+
+static void
+test_random_keys()
+{
+  testing_concurrent_btree btr;
+  fast_random r(43698);
+
+  const size_t nkeys = 10000;
+  const unsigned int maxkeylen = 1000;
+
+  set<string> keyset;
+  vector<string> keys;
+  keys.resize(nkeys);
+  for (size_t i = 0; i < nkeys; i++) {
+  retry:
+    string k = r.next_readable_string(r.next() % (maxkeylen + 1));
+    if (keyset.count(k) == 1)
+      goto retry;
+    keyset.insert(k);
+    swap(keys[i], k);
+    btr.insert(varkey(keys[i]), (typename testing_concurrent_btree::value_type) keys[i].data());
+    btr.invariant_checker();
+  }
+
+  ALWAYS_ASSERT(btr.size() == keyset.size());
+
+  for (size_t i = 0; i < nkeys; i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(varkey(keys[i]), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) keys[i].data());
+  }
+
+  test_range_scan_helper::expect ex(keyset);
+  test_range_scan_helper tester(btr, varkey(""), NULL, false, ex);
+  tester.test();
+
+#ifdef HAVE_REVERSE_RANGE_SCANS
+  const string mkey = maxkey(maxkeylen);
+  test_range_scan_helper tester_rev(btr, varkey(mkey), NULL, true, ex);
+  tester_rev.test();
+#endif
+
+  for (size_t i = 0; i < nkeys; i++) {
+    btr.remove(varkey(keys[i]));
+    btr.invariant_checker();
+  }
+  ALWAYS_ASSERT(btr.size() == 0);
+}
+
+static void
+test_insert_remove_mix()
+{
+  testing_concurrent_btree btr;
+  fast_random r(38953623328597);
+
+  // bootstrap with keys, then alternate insert/remove
+  const size_t nkeys_start = 100000;
+
+  vector<string> start_keys_v;
+  set<string> start_keys;
+  for (size_t i = 0; i < nkeys_start; i++) {
+  retry:
+    string k = r.next_readable_string(r.next() % 200);
+    if (start_keys.count(k) == 1)
+      goto retry;
+    start_keys_v.push_back(k);
+    start_keys.insert(k);
+    ALWAYS_ASSERT(btr.insert(varkey(k), (typename testing_concurrent_btree::value_type) k.data()));
+  }
+  btr.invariant_checker();
+  ALWAYS_ASSERT(btr.size() == start_keys.size());
+
+  vector<string> insert_keys_v;
+  set<string> insert_keys;
+  for (size_t i = 0; i < nkeys_start; i++) {
+  retry1:
+    string k = r.next_readable_string(r.next() % 200);
+    if (start_keys.count(k) == 1 || insert_keys.count(k) == 1)
+      goto retry1;
+    insert_keys_v.push_back(k);
+    insert_keys.insert(k);
+  }
+
+  for (size_t i = 0; i < nkeys_start; i++) {
+    ALWAYS_ASSERT(btr.remove(varkey(start_keys_v[i])));
+    ALWAYS_ASSERT(btr.insert(varkey(insert_keys_v[i]), (typename testing_concurrent_btree::value_type) insert_keys_v[i].data()));
+  }
+  btr.invariant_checker();
+  ALWAYS_ASSERT(btr.size() == insert_keys.size());
+}
+
+namespace mp_test1_ns {
+
+  static const size_t nkeys = 20000;
+
+  class ins0_worker : public btree_worker {
+  public:
+    ins0_worker(testing_concurrent_btree &btr) : btree_worker(btr) {}
+    virtual void run()
+    {
+      for (size_t i = 0; i < nkeys / 2; i++)
+        btr->insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    }
+  };
+
+  class ins1_worker : public btree_worker {
+  public:
+    ins1_worker(testing_concurrent_btree &btr) : btree_worker(btr) {}
+    virtual void run()
+    {
+      for (size_t i = nkeys / 2; i < nkeys; i++)
+        btr->insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    }
+  };
+}
+
+static void
+mp_test1()
+{
+  using namespace mp_test1_ns;
+
+  // test a bunch of concurrent inserts
+  testing_concurrent_btree btr;
+
+  ins0_worker w0(btr);
+  ins1_worker w1(btr);
+
+  w0.start(); w1.start();
+  w0.join(); w1.join();
+
+  btr.invariant_checker();
+  for (size_t i = 0; i < nkeys; i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+  ALWAYS_ASSERT(btr.size() == nkeys);
+}
+
+namespace mp_test2_ns {
+
+  static const size_t nkeys = 20000;
+
+  class rm0_worker : public btree_worker {
+  public:
+    rm0_worker(testing_concurrent_btree &btr) : btree_worker(btr) {}
+    virtual void run()
+    {
+      for (size_t i = 0; i < nkeys / 2; i++)
+        btr->remove(u64_varkey(i));
+    }
+  };
+
+  class rm1_worker : public btree_worker {
+  public:
+    rm1_worker(testing_concurrent_btree &btr) : btree_worker(btr) {}
+    virtual void run()
+    {
+      for (size_t i = nkeys / 2; i < nkeys; i++)
+        btr->remove(u64_varkey(i));
+    }
+  };
+}
+
+static void
+mp_test2()
+{
+  using namespace mp_test2_ns;
+
+  // test a bunch of concurrent removes
+  testing_concurrent_btree btr;
+
+  for (size_t i = 0; i < nkeys; i++)
+    btr.insert(u64_varkey(u64_varkey(i)), (typename testing_concurrent_btree::value_type) i);
+  btr.invariant_checker();
+
+  rm0_worker w0(btr);
+  rm1_worker w1(btr);
+
+  w0.start(); w1.start();
+  w0.join(); w1.join();
+
+  btr.invariant_checker();
+  for (size_t i = 0; i < nkeys; i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(u64_varkey(i), v));
+  }
+  ALWAYS_ASSERT(btr.size() == 0);
+}
+
+namespace mp_test3_ns {
+
+  static const size_t nkeys = 20000;
+
+  class rm0_worker : public btree_worker {
+  public:
+    rm0_worker(testing_concurrent_btree &btr) : btree_worker(btr) {}
+    virtual void run()
+    {
+      // remove the even keys
+      for (size_t i = 0; i < nkeys; i += 2)
+        btr->remove(u64_varkey(i));
+    }
+  };
+
+  class ins0_worker : public btree_worker {
+  public:
+    ins0_worker(testing_concurrent_btree &btr) : btree_worker(btr) {}
+    virtual void run()
+    {
+      // insert the odd keys
+      for (size_t i = 1; i < nkeys; i += 2)
+        btr->insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    }
+  };
+}
+
+static void
+mp_test3()
+{
+  using namespace mp_test3_ns;
+
+  // test a bunch of concurrent inserts and removes
+  testing_concurrent_btree btr;
+
+  // insert the even keys
+  for (size_t i = 0; i < nkeys; i += 2)
+    btr.insert(u64_varkey(u64_varkey(i)), (typename testing_concurrent_btree::value_type) i);
+  btr.invariant_checker();
+
+  rm0_worker w0(btr);
+  ins0_worker w1(btr);
+
+  w0.start(); w1.start();
+  w0.join(); w1.join();
+
+  btr.invariant_checker();
+
+  // should find no even keys
+  for (size_t i = 0; i < nkeys; i += 2) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(u64_varkey(i), v));
+  }
+
+  // should find all odd keys
+  for (size_t i = 1; i < nkeys; i += 2) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+
+  ALWAYS_ASSERT(btr.size() == nkeys / 2);
+}
+
+namespace mp_test4_ns {
+
+  static const size_t nkeys = 20000;
+
+  class search0_worker : public btree_worker {
+  public:
+    search0_worker(testing_concurrent_btree &btr) : btree_worker(btr) {}
+    virtual void run()
+    {
+      // search the even keys
+      for (size_t i = 0; i < nkeys; i += 2) {
+        typename testing_concurrent_btree::value_type v = 0;
+        ALWAYS_ASSERT(btr->search(u64_varkey(i), v));
+        ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+      }
+    }
+  };
+
+  class ins0_worker : public btree_worker {
+  public:
+    ins0_worker(testing_concurrent_btree &btr) : btree_worker(btr) {}
+    virtual void run()
+    {
+      // insert the odd keys
+      for (size_t i = 1; i < nkeys; i += 2)
+        btr->insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+    }
+  };
+
+  class rm0_worker : public btree_worker {
+  public:
+    rm0_worker(testing_concurrent_btree &btr) : btree_worker(btr) {}
+    virtual void run()
+    {
+      // remove and reinsert odd keys
+      for (size_t i = 1; i < nkeys; i += 2) {
+        btr->remove(u64_varkey(i));
+        btr->insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+      }
+    }
+  };
+}
+
+static void
+mp_test4()
+{
+  using namespace mp_test4_ns;
+
+  // test a bunch of concurrent searches, inserts, and removes
+  testing_concurrent_btree btr;
+
+  // insert the even keys
+  for (size_t i = 0; i < nkeys; i += 2)
+    btr.insert(u64_varkey(u64_varkey(i)), (typename testing_concurrent_btree::value_type) i);
+  btr.invariant_checker();
+
+  search0_worker w0(btr);
+  ins0_worker w1(btr);
+  rm0_worker w2(btr);
+
+  w0.start(); w1.start(); w2.start();
+  w0.join(); w1.join(); w2.join();
+
+  btr.invariant_checker();
+
+  // should find all keys
+  for (size_t i = 0; i < nkeys; i++) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(i), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) i);
+  }
+
+  ALWAYS_ASSERT(btr.size() == nkeys);
+}
+
+namespace mp_test_pinning_ns {
+  static const size_t keys_per_thread = 1000;
+  static const size_t nthreads = 4;
+  static atomic<bool> running(true);
+  class worker : public btree_worker {
+  public:
+    worker(unsigned int thread, testing_concurrent_btree &btr)
+      : btree_worker(btr), thread(thread) {}
+    virtual void
+    run()
+    {
+      rcu::s_instance.pin_current_thread(thread % coreid::num_cpus_online());
+      for (unsigned mode = 0; running.load(); mode++) {
+        for (size_t i = thread * keys_per_thread;
+             running.load() && i < (thread + 1) * keys_per_thread;
+             i++) {
+          if (mode % 2) {
+            // remove
+            btr->remove(u64_varkey(i));
+          } else {
+            // insert
+            btr->insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+          }
+        }
+      }
+    }
+  private:
+    unsigned int thread;
+  };
+}
+
+static void
+mp_test_pinning()
+{
+  using namespace mp_test_pinning_ns;
+  testing_concurrent_btree btr;
+  vector<unique_ptr<worker>> workers;
+  for (size_t i = 0; i < nthreads; i++)
+    workers.emplace_back(new worker(i, btr));
+  for (auto &p : workers)
+    p->start();
+  sleep(5);
+  running.store(false);
+  for (auto &p : workers)
+    p->join();
+  btr.invariant_checker();
+}
+
+namespace mp_test_inserts_removes_ns {
+  static const size_t keys_per_thread = 10000;
+  static const size_t nthreads = 4;
+  class worker : public btree_worker {
+  public:
+    worker(bool inserts, unsigned int thread,
+           testing_concurrent_btree &btr)
+      : btree_worker(btr), inserts(inserts), thread(thread) {}
+    virtual void
+    run()
+    {
+      for (size_t i = thread * keys_per_thread;
+           i < (thread + 1) * keys_per_thread;
+           i++) {
+        if (inserts)
+          // insert
+          btr->insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+        else
+          btr->remove(u64_varkey(i));
+      }
+    }
+  private:
+    bool inserts;
+    unsigned int thread;
+  };
+}
+
+static void
+mp_test_inserts_removes()
+{
+  using namespace mp_test_inserts_removes_ns;
+  for (size_t iter = 0; iter < 3; iter++) {
+    testing_concurrent_btree btr;
+    vector<unique_ptr<worker>> workers;
+
+    for (size_t i = 0; i < nthreads; i++)
+      workers.emplace_back(new worker(true, i, btr));
+    for (auto &p : workers)
+      p->start();
+    for (auto &p : workers)
+      p->join();
+    btr.invariant_checker();
+    workers.clear();
+
+    for (size_t i = 0; i < nthreads; i++)
+      workers.emplace_back(new worker(false, i, btr));
+    for (auto &p : workers)
+      p->start();
+    for (auto &p : workers)
+      p->join();
+    btr.invariant_checker();
+    workers.clear();
+
+    for (size_t i = 0; i < nthreads; i++) {
+      workers.emplace_back(new worker(true, i, btr));
+      workers.emplace_back(new worker(false, i, btr));
+    }
+    for (auto &p : workers)
+      p->start();
+    for (auto &p : workers)
+      p->join();
+    btr.invariant_checker();
+    workers.clear();
+  }
+}
+
+namespace mp_test5_ns {
+
+  static const size_t niters = 100000;
+  static const size_t max_key = 45;
+
+  typedef set<typename testing_concurrent_btree::key_slice> key_set;
+
+  struct summary {
+    key_set inserts;
+    key_set removes;
+  };
+
+  class worker : public btree_worker {
+  public:
+    worker(unsigned int seed, testing_concurrent_btree &btr) : btree_worker(btr), seed(seed) {}
+    virtual void run()
+    {
+      unsigned int s = seed;
+      // 60% search, 30% insert, 10% remove
+      for (size_t i = 0; i < niters; i++) {
+        double choice = double(rand_r(&s)) / double(RAND_MAX);
+        typename testing_concurrent_btree::key_slice k = rand_r(&s) % max_key;
+        if (choice < 0.6) {
+          typename testing_concurrent_btree::value_type v = 0;
+          if (btr->search(u64_varkey(k), v))
+            ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) k);
+        } else if (choice < 0.9) {
+          btr->insert(u64_varkey(k), (typename testing_concurrent_btree::value_type) k);
+          sum.inserts.insert(k);
+        } else {
+          btr->remove(u64_varkey(k));
+          sum.removes.insert(k);
+        }
+      }
+    }
+    summary sum;
+  private:
+    unsigned int seed;
+  };
+}
+
+static void
+mp_test5()
+{
+  using namespace mp_test5_ns;
+
+  testing_concurrent_btree btr;
+
+  worker w0(2145906155, btr);
+  worker w1(409088773, btr);
+  worker w2(4199288861, btr);
+  worker w3(496889962, btr);
+
+  w0.start(); w1.start(); w2.start(); w3.start();
+  w0.join(); w1.join(); w2.join(); w3.join();
+
+  summary *s0, *s1, *s2, *s3;
+  s0 = (summary *) &w0.sum;
+  s1 = (summary *) &w1.sum;
+  s2 = (summary *) &w2.sum;
+  s3 = (summary *) &w3.sum;
+
+  key_set inserts;
+  key_set removes;
+
+  summary *sums[] = { s0, s1, s2, s3 };
+  for (size_t i = 0; i < ARRAY_NELEMS(sums); i++) {
+    inserts.insert(sums[i]->inserts.begin(), sums[i]->inserts.end());
+   removes.insert(sums[i]->removes.begin(), sums[i]->removes.end());
+  }
+
+  cerr << "num_inserts: " << inserts.size() << endl;
+  cerr << "num_removes: " << removes.size() << endl;
+
+  for (key_set::iterator it = inserts.begin(); it != inserts.end(); ++it) {
+    if (removes.count(*it) == 1)
+      continue;
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(*it), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) *it);
+  }
+
+  btr.invariant_checker();
+  cerr << "btr size: " << btr.size() << endl;
+}
+
+namespace mp_test6_ns {
+  static const size_t nthreads = 16;
+  static const size_t ninsertkeys_perthread = 100000;
+  static const size_t nremovekeys_perthread = 100000;
+
+  typedef vector<typename testing_concurrent_btree::key_slice> key_vec;
+
+  class insert_worker : public btree_worker {
+  public:
+    insert_worker(const vector<typename testing_concurrent_btree::key_slice> &keys, testing_concurrent_btree &btr)
+      : btree_worker(btr), keys(keys) {}
+    virtual void run()
+    {
+      for (size_t i = 0; i < keys.size(); i++)
+        btr->insert(u64_varkey(keys[i]), (typename testing_concurrent_btree::value_type) keys[i]);
+    }
+  private:
+    vector<typename testing_concurrent_btree::key_slice> keys;
+  };
+
+  class remove_worker : public btree_worker {
+  public:
+    remove_worker(const vector<typename testing_concurrent_btree::key_slice> &keys, testing_concurrent_btree &btr)
+      : btree_worker(btr), keys(keys) {}
+    virtual void run()
+    {
+      for (size_t i = 0; i < keys.size(); i++)
+        btr->remove(u64_varkey(keys[i]));
+    }
+  private:
+    vector<typename testing_concurrent_btree::key_slice> keys;
+  };
+}
+
+static void
+mp_test6()
+{
+  using namespace mp_test6_ns;
+
+  testing_concurrent_btree btr;
+  vector<key_vec> inps;
+  set<unsigned long> insert_keys, remove_keys;
+
+  fast_random r(87643982);
+  for (size_t i = 0; i < nthreads / 2; i++) {
+    key_vec inp;
+    for (size_t j = 0; j < ninsertkeys_perthread; j++) {
+      unsigned long k = r.next();
+      insert_keys.insert(k);
+      inp.push_back(k);
+    }
+    inps.push_back(inp);
+  }
+  for (size_t i = nthreads / 2; i < nthreads; i++) {
+    key_vec inp;
+    for (size_t j = 0; j < nremovekeys_perthread;) {
+      unsigned long k = r.next();
+      if (insert_keys.count(k) == 1)
+        continue;
+      btr.insert(u64_varkey(k), (typename testing_concurrent_btree::value_type) k);
+      remove_keys.insert(k);
+      inp.push_back(k);
+      j++;
+    }
+    inps.push_back(inp);
+  }
+
+  vector<btree_worker*> workers;
+  for (size_t i = 0; i < nthreads / 2; i++)
+    workers.push_back(new insert_worker(inps[i], btr));
+  for (size_t i = nthreads / 2; i < nthreads; i++)
+    workers.push_back(new remove_worker(inps[i], btr));
+  for (size_t i = 0; i < nthreads; i++)
+    workers[i]->start();
+  for (size_t i = 0; i < nthreads; i++)
+    workers[i]->join();
+
+  btr.invariant_checker();
+
+  ALWAYS_ASSERT(btr.size() == insert_keys.size());
+  for (set<unsigned long>::iterator it = insert_keys.begin();
+       it != insert_keys.end(); ++it) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(u64_varkey(*it), v));
+    ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) *it);
+  }
+  for (set<unsigned long>::iterator it = remove_keys.begin();
+       it != remove_keys.end(); ++it) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(u64_varkey(*it), v));
+  }
+
+  for (size_t i = 0; i < nthreads; i++)
+    delete workers[i];
+}
+
+namespace mp_test7_ns {
+  static const size_t nkeys = 50;
+  static volatile bool running = false;
+
+  typedef vector<typename testing_concurrent_btree::key_slice> key_vec;
+
+  struct scan_callback {
+    typedef vector<
+      pair< std::string, typename testing_concurrent_btree::value_type > > kv_vec;
+    scan_callback(kv_vec *data) : data(data) {}
+    inline bool
+    operator()(const typename testing_concurrent_btree::string_type &k, typename testing_concurrent_btree::value_type v) const
+    {
+      //ALWAYS_ASSERT(data->empty() || data->back().first < k.str());
+      std::string k_str(k);
+      if (!data->empty() && data->back().first >= k_str) {
+        cerr << "prev: <" << hexify(data->back().first) << ">" << endl;
+        cerr << "cur : <" << hexify(k_str) << ">" << endl;
+        ALWAYS_ASSERT(false);
+      }
+      data->push_back(make_pair(std::move(k_str), v));
+      return true;
+    }
+    kv_vec *data;
+  };
+
+  class lookup_worker : public btree_worker {
+  public:
+    lookup_worker(unsigned long seed, const key_vec &keys, testing_concurrent_btree &btr)
+      : btree_worker(btr), seed(seed), keys(keys)
+    {}
+    virtual void run()
+    {
+      fast_random r(seed);
+      while (running) {
+        uint64_t k = keys[r.next() % keys.size()];
+        typename testing_concurrent_btree::value_type v = NULL;
+        ALWAYS_ASSERT(btr->search(u64_varkey(k), v));
+        ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) k);
+      }
+    }
+    unsigned long seed;
+    key_vec keys;
+  };
+
+  class scan_worker : public btree_worker {
+  public:
+    scan_worker(const key_vec &keys, testing_concurrent_btree &btr)
+      : btree_worker(btr), keys(keys)
+    {}
+    virtual void run()
+    {
+      while (running) {
+        scan_callback::kv_vec data;
+        scan_callback cb(&data);
+        btr->search_range(u64_varkey(nkeys / 2), NULL, cb);
+        set<typename testing_concurrent_btree::string_type> scan_keys;
+        std::string prev;
+        for (size_t i = 0; i < data.size(); i++) {
+          if (i != 0) {
+            ALWAYS_ASSERT(data[i].first != prev);
+            ALWAYS_ASSERT(data[i].first > prev);
+          }
+          scan_keys.insert(data[i].first);
+          prev = data[i].first;
+        }
+        for (size_t i = 0; i < keys.size(); i++) {
+          if (keys[i] < (nkeys / 2))
+            continue;
+          ALWAYS_ASSERT(scan_keys.count(u64_varkey(keys[i]).str()) == 1);
+        }
+      }
+    }
+    key_vec keys;
+  };
+
+  class mod_worker : public btree_worker {
+  public:
+    mod_worker(const key_vec &keys, testing_concurrent_btree &btr)
+      : btree_worker(btr), keys(keys)
+    {}
+    virtual void run()
+    {
+      bool insert = true;
+      for (size_t i = 0; running; i = (i + 1) % keys.size(), insert = !insert) {
+        if (insert)
+          btr->insert(u64_varkey(keys[i]), (typename testing_concurrent_btree::value_type) keys[i]);
+        else
+          btr->remove(u64_varkey(keys[i]));
+      }
+    }
+    key_vec keys;
+  };
+}
+
+static void
+mp_test7()
+{
+  using namespace mp_test7_ns;
+  fast_random r(904380439);
+  key_vec lookup_keys;
+  key_vec mod_keys;
+  for (size_t i = 0; i < nkeys; i++) {
+    if (r.next() % 2)
+      mod_keys.push_back(i);
+    else
+      lookup_keys.push_back(i);
+  }
+
+  testing_concurrent_btree btr;
+  for (size_t i = 0; i < lookup_keys.size(); i++)
+    btr.insert(u64_varkey(lookup_keys[i]), (typename testing_concurrent_btree::value_type) lookup_keys[i]);
+  btr.invariant_checker();
+
+  lookup_worker w0(2398430, lookup_keys, btr);
+  lookup_worker w1(8532, lookup_keys, btr);
+  lookup_worker w2(23, lookup_keys, btr);
+  lookup_worker w3(1328209843, lookup_keys, btr);
+  scan_worker w4(lookup_keys, btr);
+  scan_worker w5(lookup_keys, btr);
+  mod_worker w6(mod_keys, btr);
+
+  running = true;
+  COMPILER_MEMORY_FENCE;
+  w0.start(); w1.start(); w2.start(); w3.start(); w4.start(); w5.start(); w6.start();
+  sleep(10);
+  COMPILER_MEMORY_FENCE;
+  running = false;
+  COMPILER_MEMORY_FENCE;
+  w0.join(); w1.join(); w2.join(); w3.join(); w4.join(); w5.join(); w6.join();
+}
+
+namespace mp_test8_ns {
+  static const size_t nthreads = 16;
+  static const size_t ninsertkeys_perthread = 100000;
+  static const size_t nremovekeys_perthread = 100000;
+
+  typedef vector<string> key_vec;
+
+  class insert_worker : public btree_worker {
+  public:
+    insert_worker(const vector<string> &keys, testing_concurrent_btree &btr)
+      : btree_worker(btr), keys(keys) {}
+    virtual void run()
+    {
+      for (size_t i = 0; i < keys.size(); i++)
+        ALWAYS_ASSERT(btr->insert(varkey(keys[i]), (typename testing_concurrent_btree::value_type) keys[i].data()));
+    }
+  private:
+    vector<string> keys;
+  };
+
+  class remove_worker : public btree_worker {
+  public:
+    remove_worker(const vector<string> &keys, testing_concurrent_btree &btr)
+      : btree_worker(btr), keys(keys) {}
+    virtual void run()
+    {
+      for (size_t i = 0; i < keys.size(); i++)
+        ALWAYS_ASSERT(btr->remove(varkey(keys[i])));
+    }
+  private:
+    vector<string> keys;
+  };
+}
+
+static void
+mp_test8()
+{
+  using namespace mp_test8_ns;
+
+  testing_concurrent_btree btr;
+  vector<key_vec> inps;
+  set<string> insert_keys, remove_keys;
+
+  fast_random r(83287583);
+  for (size_t i = 0; i < nthreads / 2; i++) {
+    key_vec inp;
+    for (size_t j = 0; j < ninsertkeys_perthread; j++) {
+    retry:
+      string k = r.next_string(r.next() % 200);
+      if (insert_keys.count(k) == 1)
+        goto retry;
+      insert_keys.insert(k);
+      inp.push_back(k);
+    }
+    inps.push_back(inp);
+  }
+  for (size_t i = nthreads / 2; i < nthreads; i++) {
+    key_vec inp;
+    for (size_t j = 0; j < nremovekeys_perthread;) {
+      string k = r.next_string(r.next() % 200);
+      if (insert_keys.count(k) == 1 || remove_keys.count(k) == 1)
+        continue;
+      ALWAYS_ASSERT(btr.insert(varkey(k), (typename testing_concurrent_btree::value_type) k.data()));
+      remove_keys.insert(k);
+      inp.push_back(k);
+      j++;
+    }
+    inps.push_back(inp);
+  }
+
+  btr.invariant_checker();
+
+  vector<btree_worker*> workers;
+  for (size_t i = 0; i < nthreads / 2; i++)
+    workers.push_back(new insert_worker(inps[i], btr));
+  for (size_t i = nthreads / 2; i < nthreads; i++)
+    workers.push_back(new remove_worker(inps[i], btr));
+  for (size_t i = 0; i < nthreads; i++)
+    workers[i]->start();
+  for (size_t i = 0; i < nthreads; i++)
+    workers[i]->join();
+
+  btr.invariant_checker();
+
+  ALWAYS_ASSERT(btr.size() == insert_keys.size());
+  for (set<string>::iterator it = insert_keys.begin();
+       it != insert_keys.end(); ++it) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(varkey(*it), v));
+  }
+  for (set<string>::iterator it = remove_keys.begin();
+       it != remove_keys.end(); ++it) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(varkey(*it), v));
+  }
+
+  for (size_t i = 0; i < nthreads; i++)
+    delete workers[i];
+}
+
+namespace mp_test_long_keys_ns {
+  static const size_t nthreads = 16;
+  static const size_t ninsertkeys_perthread = 500000;
+  static const size_t nremovekeys_perthread = 500000;
+
+  typedef vector<string> key_vec;
+
+  class insert_worker : public btree_worker {
+  public:
+    insert_worker(const vector<string> &keys, testing_concurrent_btree &btr)
+      : btree_worker(btr), keys(keys) {}
+    virtual void run()
+    {
+      for (size_t i = 0; i < keys.size(); i++)
+        ALWAYS_ASSERT(btr->insert(varkey(keys[i]), (typename testing_concurrent_btree::value_type) keys[i].data()));
+    }
+  private:
+    vector<string> keys;
+  };
+
+  class remove_worker : public btree_worker {
+  public:
+    remove_worker(const vector<string> &keys, testing_concurrent_btree &btr)
+      : btree_worker(btr), keys(keys) {}
+    virtual void run()
+    {
+      for (size_t i = 0; i < keys.size(); i++)
+        ALWAYS_ASSERT(btr->remove(varkey(keys[i])));
+    }
+  private:
+    vector<string> keys;
+  };
+
+  static volatile bool running = false;
+
+  class scan_worker : public btree_worker {
+  public:
+    scan_worker(const set<string> &ex, testing_concurrent_btree &btr, bool reverse)
+      : btree_worker(btr), ex(ex), reverse_(reverse) {}
+    virtual void run()
+    {
+      const string mkey = maxkey(200+9);
+      while (running) {
+        if (!reverse_) {
+          test_range_scan_helper tester(*btr, varkey(""), NULL, false,
+              ex, test_range_scan_helper::EXPECT_ATLEAST);
+          tester.test();
+        } else {
+          test_range_scan_helper tester(*btr, varkey(mkey), NULL, true,
+              ex, test_range_scan_helper::EXPECT_ATLEAST);
+          tester.test();
+        }
+      }
+    }
+  private:
+    test_range_scan_helper::expect ex;
+    bool reverse_;
+  };
+}
+
+static void
+mp_test_long_keys()
+{
+  // all keys at least 9-bytes long
+  using namespace mp_test_long_keys_ns;
+
+  testing_concurrent_btree btr;
+  vector<key_vec> inps;
+  set<string> existing_keys, insert_keys, remove_keys;
+
+  fast_random r(189230589352);
+  for (size_t i = 0; i < 10000; i++) {
+  retry0:
+    string k = r.next_string((r.next() % 200) + 9);
+    if (existing_keys.count(k) == 1)
+      goto retry0;
+    existing_keys.insert(k);
+    ALWAYS_ASSERT(btr.insert(varkey(k), (typename testing_concurrent_btree::value_type) k.data()));
+  }
+  ALWAYS_ASSERT(btr.size() == existing_keys.size());
+
+  for (size_t i = 0; i < nthreads / 2; i++) {
+    key_vec inp;
+    for (size_t j = 0; j < ninsertkeys_perthread; j++) {
+    retry:
+      string k = r.next_string((r.next() % 200) + 9);
+      if (insert_keys.count(k) == 1 || existing_keys.count(k) == 1)
+        goto retry;
+      insert_keys.insert(k);
+      inp.push_back(k);
+    }
+    inps.push_back(inp);
+  }
+
+  for (size_t i = nthreads / 2; i < nthreads; i++) {
+    key_vec inp;
+    for (size_t j = 0; j < nremovekeys_perthread;) {
+      string k = r.next_string((r.next() % 200) + 9);
+      if (insert_keys.count(k) == 1 || existing_keys.count(k) == 1 || remove_keys.count(k) == 1)
+        continue;
+      ALWAYS_ASSERT(btr.insert(varkey(k), (typename testing_concurrent_btree::value_type) k.data()));
+      remove_keys.insert(k);
+      inp.push_back(k);
+      j++;
+    }
+    inps.push_back(inp);
+  }
+
+  ALWAYS_ASSERT(btr.size() == (insert_keys.size() + existing_keys.size()));
+  btr.invariant_checker();
+
+  vector<btree_worker*> workers, running_workers;
+  running = true;
+  for (size_t i = 0; i < nthreads / 2; i++)
+    workers.push_back(new insert_worker(inps[i], btr));
+  for (size_t i = nthreads / 2; i < nthreads; i++)
+    workers.push_back(new remove_worker(inps[i], btr));
+  for (size_t i = 0; i < 4; i++)
+    running_workers.push_back(new scan_worker(existing_keys, btr, (i % 2)));
+  for (size_t i = 0; i < nthreads; i++)
+    workers[i]->start();
+  for (size_t i = 0; i < running_workers.size(); i++)
+    running_workers[i]->start();
+  for (size_t i = 0; i < nthreads; i++)
+    workers[i]->join();
+  running = false;
+  for (size_t i = 0; i < running_workers.size(); i++)
+    running_workers[i]->join();
+
+  btr.invariant_checker();
+
+  ALWAYS_ASSERT(btr.size() == (insert_keys.size() + existing_keys.size()));
+  for (set<string>::iterator it = insert_keys.begin();
+       it != insert_keys.end(); ++it) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(btr.search(varkey(*it), v));
+  }
+  for (set<string>::iterator it = remove_keys.begin();
+       it != remove_keys.end(); ++it) {
+    typename testing_concurrent_btree::value_type v = 0;
+    ALWAYS_ASSERT(!btr.search(varkey(*it), v));
+  }
+
+  for (size_t i = 0; i < nthreads; i++)
+    delete workers[i];
+  for (size_t i = 0; i < running_workers.size(); i++)
+    delete running_workers[i];
+}
+
+static void perf_test() UNUSED;
+static void
+perf_test()
+{
+  const size_t nrecs = 10000000;
+  const size_t nlookups = 10000000;
+
+  {
+    srand(9876);
+    map<uint64_t, uint64_t> m;
+    {
+      scoped_rate_timer t("map insert", nrecs);
+      for (size_t i = 0; i < nrecs; i++)
+        m[i] = i;
+    }
+    {
+      scoped_rate_timer t("map random lookups", nlookups);
+      for (size_t i = 0; i < nlookups; i++) {
+        //uint64_t key = rand() % nrecs;
+        uint64_t key = i;
+        map<uint64_t, uint64_t>::iterator it =
+          m.find(key);
+        ALWAYS_ASSERT(it != m.end());
+      }
+    }
+  }
+
+  {
+    srand(9876);
+    testing_concurrent_btree btr;
+    {
+      scoped_rate_timer t("btree insert", nrecs);
+      for (size_t i = 0; i < nrecs; i++)
+        btr.insert(u64_varkey(u64_varkey(i)), (typename testing_concurrent_btree::value_type) i);
+    }
+    {
+      scoped_rate_timer t("btree random lookups", nlookups);
+      for (size_t i = 0; i < nlookups; i++) {
+        //uint64_t key = rand() % nrecs;
+        uint64_t key = i;
+        typename testing_concurrent_btree::value_type v = 0;
+        ALWAYS_ASSERT(btr.search(u64_varkey(key), v));
+      }
+    }
+  }
+}
+
+namespace read_only_perf_test_ns {
+  const size_t nkeys = 140000000; // 140M
+  //const size_t nkeys = 100000; // 100K
+
+  unsigned long seeds[] = {
+    9576455804445224191ULL,
+    3303315688255411629ULL,
+    3116364238170296072ULL,
+    641702699332002535ULL,
+    17755947590284612420ULL,
+    13349066465957081273ULL,
+    16389054441777092823ULL,
+    2687412585397891607ULL,
+    16665670053534306255ULL,
+    5166823197462453937ULL,
+    1252059952779729626ULL,
+    17962022827457676982ULL,
+    940911318964853784ULL,
+    479878990529143738ULL,
+    250864516707124695ULL,
+    8507722621803716653ULL,
+  };
+
+  volatile bool running = false;
+
+  class worker : public btree_worker {
+  public:
+    worker(unsigned int seed, testing_concurrent_btree &btr) : btree_worker(btr), n(0), seed(seed) {}
+    virtual void run()
+    {
+      fast_random r(seed);
+      while (running) {
+        typename testing_concurrent_btree::key_slice k = r.next() % nkeys;
+        typename testing_concurrent_btree::value_type v = 0;
+        ALWAYS_ASSERT(btr->search(u64_varkey(k), v));
+        ALWAYS_ASSERT(v == (typename testing_concurrent_btree::value_type) k);
+        n++;
+      }
+    }
+    uint64_t n;
+  private:
+    unsigned int seed;
+  };
+}
+
+static void read_only_perf_test() UNUSED;
+static void
+read_only_perf_test()
+{
+  using namespace read_only_perf_test_ns;
+
+  testing_concurrent_btree btr;
+
+  for (size_t i = 0; i < nkeys; i++)
+    btr.insert(u64_varkey(i), (typename testing_concurrent_btree::value_type) i);
+  cerr << "btree loaded, test starting" << endl;
+
+  vector<worker *> workers;
+  for (size_t i = 0; i < ARRAY_NELEMS(seeds); i++)
+    workers.push_back(new worker(seeds[i], btr));
+
+  running = true;
+  util::timer t;
+  COMPILER_MEMORY_FENCE;
+  for (size_t i = 0; i < ARRAY_NELEMS(seeds); i++)
+    workers[i]->start();
+  sleep(30);
+  COMPILER_MEMORY_FENCE;
+  running = false;
+  COMPILER_MEMORY_FENCE;
+  uint64_t total_n = 0;
+  for (size_t i = 0; i < ARRAY_NELEMS(seeds); i++) {
+    workers[i]->join();
+    total_n += workers[i]->n;
+    delete workers[i];
+  }
+
+  double agg_throughput = double(total_n) / (double(t.lap()) / 1000000.0);
+  double avg_per_core_throughput = agg_throughput / double(ARRAY_NELEMS(seeds));
+
+  cerr << "agg_read_throughput: " << agg_throughput << " gets/sec" << endl;
+  cerr << "avg_per_core_read_throughput: " << avg_per_core_throughput << " gets/sec/core" << endl;
+}
+
+namespace write_only_perf_test_ns {
+  const size_t nkeys = 140000000; // 140M
+  //const size_t nkeys = 100000; // 100K
+
+  unsigned long seeds[] = {
+    17188055221422272641ULL,
+    915721317773011804ULL,
+    11607688859420148202ULL,
+    16566896965529356730ULL,
+    3687473034241167633ULL,
+    1168118474092824592ULL,
+    912212972587845337ULL,
+    890657129662032640ULL,
+    7557640044845923769ULL,
+    9490577770668659131ULL,
+    14081403972130650060ULL,
+    14956552848279294368ULL,
+    8669268465391111275ULL,
+    1904251150166743550ULL,
+    4418832947790992405ULL,
+    9558684485283258563ULL,
+  };
+
+  class worker : public btree_worker {
+  public:
+    worker(unsigned int seed, testing_concurrent_btree &btr) : btree_worker(btr), seed(seed) {}
+    virtual void run()
+    {
+      fast_random r(seed);
+      for (size_t i = 0; i < nkeys / ARRAY_NELEMS(seeds); i++) {
+        typename testing_concurrent_btree::key_slice k = r.next() % nkeys;
+        btr->insert(u64_varkey(k), (typename testing_concurrent_btree::value_type) k);
+      }
+    }
+  private:
+    unsigned int seed;
+  };
+}
+
+static void write_only_perf_test() UNUSED;
+static void
+write_only_perf_test()
+{
+  using namespace write_only_perf_test_ns;
+
+  testing_concurrent_btree btr;
+
+  vector<worker *> workers;
+  for (size_t i = 0; i < ARRAY_NELEMS(seeds); i++)
+    workers.push_back(new worker(seeds[i], btr));
+
+  util::timer t;
+  for (size_t i = 0; i < ARRAY_NELEMS(seeds); i++)
+    workers[i]->start();
+  for (size_t i = 0; i < ARRAY_NELEMS(seeds); i++) {
+    workers[i]->join();
+    delete workers[i];
+  }
+
+  double agg_throughput = double(nkeys) / (double(t.lap()) / 1000000.0);
+  double avg_per_core_throughput = agg_throughput / double(ARRAY_NELEMS(seeds));
+
+  cerr << "agg_write_throughput: " << agg_throughput << " puts/sec" << endl;
+  cerr << "avg_per_core_write_throughput: " << avg_per_core_throughput << " puts/sec/core" << endl;
+}
+
+void
+TestConcurrentBtreeFast()
+{
+  test1();
+  test2();
+  test3();
+  test4();
+  test6();
+  test7();
+  test_varlen_single_layer();
+  test_varlen_multi_layer();
+  test_two_layer();
+  test_two_layer_range_scan();
+  test_multi_layer_scan();
+  test_null_keys();
+  test_null_keys_2();
+  test_random_keys();
+  test_insert_remove_mix();
+  mp_test_pinning();
+  mp_test_inserts_removes();
+  cout << "testing_concurrent_btree::TestFast passed" << endl;
+}
+
+void
+TestConcurrentBtreeSlow()
+{
+  test5();
+  mp_test1();
+  mp_test2();
+  mp_test3();
+  mp_test4();
+  mp_test5();
+  mp_test6();
+  mp_test7();
+  mp_test8();
+  mp_test_long_keys();
+  //perf_test();
+  //read_only_perf_test();
+  //write_only_perf_test();
+  cout << "testing_concurrent_btree::TestSlow passed" << endl;
+}
diff --git a/silo/btree.h b/silo/btree.h
new file mode 100644 (file)
index 0000000..79a53ad
--- /dev/null
@@ -0,0 +1,1794 @@
+#pragma once
+
+#include <assert.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <utility>
+#include <atomic>
+#include <thread>
+
+#include "log2.hh"
+#include "ndb_type_traits.h"
+#include "varkey.h"
+#include "counter.h"
+#include "macros.h"
+#include "prefetch.h"
+#include "amd64.h"
+#include "rcu.h"
+#include "util.h"
+#include "small_vector.h"
+#include "ownership_checker.h"
+
+namespace private_ {
+  template <typename T, typename P> struct u64manip;
+  template <typename P>
+  struct u64manip<uint64_t, P> {
+    static inline uint64_t Load(uint64_t t) { return t; }
+    static inline void Store(uint64_t &t, uint64_t v) { t = v; }
+    static inline uint64_t
+    Lock(uint64_t &t)
+    {
+#ifdef CHECK_INVARIANTS
+      INVARIANT(!P::IsLocked(t));
+      t |= P::HDR_LOCKED_MASK;
+      return t;
+#endif
+      return 0;
+    }
+    static inline uint64_t
+    LockWithSpinCount(uint64_t &t, unsigned &spins)
+    {
+      const uint64_t ret = Lock(t);
+      spins = 0;
+      return ret;
+    }
+    static inline void
+    Unlock(uint64_t &t)
+    {
+#ifdef CHECK_INVARIANTS
+      INVARIANT(P::IsLocked(t));
+      t &= ~(P::HDR_LOCKED_MASK | P::HDR_MODIFYING_MASK);
+#endif
+    }
+    static inline uint64_t
+    StableVersion(uint64_t t)
+    {
+      INVARIANT(!P::IsModifying(t));
+      return t;
+    }
+    static inline bool
+    CheckVersion(uint64_t t, uint64_t stablev)
+    {
+      INVARIANT(!P::IsModifying(stablev));
+      INVARIANT((t & ~P::HDR_LOCKED_MASK) == (stablev & ~P::HDR_LOCKED_MASK));
+      return true;
+    }
+  };
+  template <typename P>
+  struct u64manip<std::atomic<uint64_t>, P> {
+    static inline uint64_t
+    Load(const std::atomic<uint64_t> &t)
+    {
+      return t.load(std::memory_order_acquire);
+    }
+    static inline void
+    Store(std::atomic<uint64_t> &t, uint64_t v)
+    {
+      t.store(v, std::memory_order_release);
+    }
+    static inline uint64_t
+    Lock(std::atomic<uint64_t> &t)
+    {
+#ifdef SPINLOCK_BACKOFF
+      uint64_t backoff_shift = 0;
+#endif
+      uint64_t v = Load(t);
+      while ((v & P::HDR_LOCKED_MASK) ||
+             !t.compare_exchange_strong(v, v | P::HDR_LOCKED_MASK)) {
+#ifdef SPINLOCK_BACKOFF
+        if (backoff_shift < 63)
+          backoff_shift++;
+        uint64_t spins = (1UL << backoff_shift) * BACKOFF_SPINS_FACTOR;
+        while (spins) {
+          nop_pause();
+          spins--;
+        }
+#else
+        nop_pause();
+#endif
+        v = Load(t);
+      }
+      COMPILER_MEMORY_FENCE;
+      return v;
+    }
+    static inline uint64_t
+    LockWithSpinCount(std::atomic<uint64_t> &t, unsigned &spins)
+    {
+#ifdef SPINLOCK_BACKOFF
+      uint64_t backoff_shift = 0;
+#endif
+      spins = 0;
+      uint64_t v = Load(t);
+      while ((v & P::HDR_LOCKED_MASK) ||
+             !t.compare_exchange_strong(v, v | P::HDR_LOCKED_MASK)) {
+#ifdef SPINLOCK_BACKOFF
+        if (backoff_shift < 63)
+          backoff_shift++;
+        uint64_t backoff_spins = (1UL << backoff_shift) * BACKOFF_SPINS_FACTOR;
+        while (backoff_spins) {
+          nop_pause();
+          backoff_spins--;
+        }
+#else
+        nop_pause();
+#endif
+        v = Load(t);
+        spins++;
+      }
+      COMPILER_MEMORY_FENCE;
+      return v;
+    }
+    static inline void
+    Unlock(std::atomic<uint64_t> &v)
+    {
+      INVARIANT(P::IsLocked(v));
+      const uint64_t oldh = Load(v);
+      uint64_t h = oldh;
+      bool newv = false;
+      if ((h & P::HDR_MODIFYING_MASK) ||
+          (h & P::HDR_DELETING_MASK)) {
+        newv = true;
+        const uint64_t n = (h & P::HDR_VERSION_MASK) >> P::HDR_VERSION_SHIFT;
+        h &= ~P::HDR_VERSION_MASK;
+        h |= (((n + 1) << P::HDR_VERSION_SHIFT) & P::HDR_VERSION_MASK);
+      }
+      // clear locked + modifying bits
+      h &= ~(P::HDR_LOCKED_MASK | P::HDR_MODIFYING_MASK);
+      if (newv)
+        INVARIANT(!CheckVersion(oldh, h));
+      INVARIANT(!(h & P::HDR_LOCKED_MASK));
+      INVARIANT(!(h & P::HDR_MODIFYING_MASK));
+      COMPILER_MEMORY_FENCE;
+      Store(v, h);
+    }
+    static inline uint64_t
+    StableVersion(const std::atomic<uint64_t> &t)
+    {
+      uint64_t v = Load(t);
+      while ((v & P::HDR_MODIFYING_MASK)) {
+        nop_pause();
+        v = Load(t);
+      }
+      COMPILER_MEMORY_FENCE;
+      return v;
+    }
+    static inline bool
+    CheckVersion(uint64_t t, uint64_t stablev)
+    {
+      INVARIANT(!(stablev & P::HDR_MODIFYING_MASK));
+      COMPILER_MEMORY_FENCE;
+      return (t & ~P::HDR_LOCKED_MASK) ==
+             (stablev & ~P::HDR_LOCKED_MASK);
+    }
+  };
+}
+
+/**
+ * manipulates a btree version
+ *
+ * hdr bits: layout is (actual bytes depend on the NKeysPerNode parameter)
+ *
+ * <-- low bits
+ * [type | key_slots_used | locked | is_root | modifying | deleting | version ]
+ * [0:1  | 1:5            | 5:6    | 6:7     | 7:8       | 8:9      | 9:64    ]
+ *
+ * bit invariants:
+ *   1) modifying => locked
+ *   2) deleting  => locked
+ *
+ * WARNING: the correctness of our concurrency scheme relies on being able
+ * to do a memory reads/writes from/to hdr atomically. x86 architectures
+ * guarantee that aligned writes are atomic (see intel spec)
+ */
+template <typename VersionType, unsigned NKeysPerNode>
+class btree_version_manip {
+
+  typedef
+    typename private_::typeutil<VersionType>::func_param_type
+    LoadVersionType;
+
+  typedef
+    private_::u64manip<
+      VersionType,
+      btree_version_manip<VersionType, NKeysPerNode>>
+    U64Manip;
+
+  static inline constexpr uint64_t
+  LowMask(uint64_t nbits)
+  {
+    return (1UL << nbits) - 1UL;
+  }
+
+public:
+
+  static const uint64_t HDR_TYPE_BITS = 1;
+  static const uint64_t HDR_TYPE_MASK = 0x1;
+
+  static const uint64_t HDR_KEY_SLOTS_SHIFT = HDR_TYPE_BITS;
+  static const uint64_t HDR_KEY_SLOTS_BITS = ceil_log2_const(NKeysPerNode);
+  static const uint64_t HDR_KEY_SLOTS_MASK = LowMask(HDR_KEY_SLOTS_BITS) << HDR_KEY_SLOTS_SHIFT;
+
+  static const uint64_t HDR_LOCKED_SHIFT = HDR_KEY_SLOTS_SHIFT + HDR_KEY_SLOTS_BITS;
+  static const uint64_t HDR_LOCKED_BITS = 1;
+  static const uint64_t HDR_LOCKED_MASK = LowMask(HDR_LOCKED_BITS) << HDR_LOCKED_SHIFT;
+
+  static const uint64_t HDR_IS_ROOT_SHIFT = HDR_LOCKED_SHIFT + HDR_LOCKED_BITS;
+  static const uint64_t HDR_IS_ROOT_BITS = 1;
+  static const uint64_t HDR_IS_ROOT_MASK = LowMask(HDR_IS_ROOT_BITS) << HDR_IS_ROOT_SHIFT;
+
+  static const uint64_t HDR_MODIFYING_SHIFT = HDR_IS_ROOT_SHIFT + HDR_IS_ROOT_BITS;
+  static const uint64_t HDR_MODIFYING_BITS = 1;
+  static const uint64_t HDR_MODIFYING_MASK = LowMask(HDR_MODIFYING_BITS) << HDR_MODIFYING_SHIFT;
+
+  static const uint64_t HDR_DELETING_SHIFT = HDR_MODIFYING_SHIFT + HDR_MODIFYING_BITS;
+  static const uint64_t HDR_DELETING_BITS = 1;
+  static const uint64_t HDR_DELETING_MASK = LowMask(HDR_DELETING_BITS) << HDR_DELETING_SHIFT;
+
+  static const uint64_t HDR_VERSION_SHIFT = HDR_DELETING_SHIFT + HDR_DELETING_BITS;
+  static const uint64_t HDR_VERSION_MASK = ((uint64_t)-1) << HDR_VERSION_SHIFT;
+
+  // sanity checks
+  static_assert(NKeysPerNode >= 1, "XX");
+
+  static_assert(std::numeric_limits<uint64_t>::max() == (
+      HDR_TYPE_MASK |
+      HDR_KEY_SLOTS_MASK |
+      HDR_LOCKED_MASK |
+      HDR_IS_ROOT_MASK |
+      HDR_MODIFYING_MASK |
+      HDR_DELETING_MASK |
+      HDR_VERSION_MASK
+        ), "XX");
+
+  static_assert( !(HDR_TYPE_MASK & HDR_KEY_SLOTS_MASK) , "XX");
+  static_assert( !(HDR_KEY_SLOTS_MASK & HDR_LOCKED_MASK) , "XX");
+  static_assert( !(HDR_LOCKED_MASK & HDR_IS_ROOT_MASK) , "XX");
+  static_assert( !(HDR_IS_ROOT_MASK & HDR_MODIFYING_MASK) , "XX");
+  static_assert( !(HDR_MODIFYING_MASK & HDR_DELETING_MASK) , "XX");
+  static_assert( !(HDR_DELETING_MASK & HDR_VERSION_MASK) , "XX");
+
+  // low level ops
+
+  static inline uint64_t
+  Load(LoadVersionType v)
+  {
+    return U64Manip::Load(v);
+  }
+
+  static inline void
+  Store(VersionType &t, uint64_t v)
+  {
+    U64Manip::Store(t, v);
+  }
+
+  // accessors
+
+  static inline bool
+  IsLeafNode(LoadVersionType v)
+  {
+    return (Load(v) & HDR_TYPE_MASK) == 0;
+  }
+
+  static inline bool
+  IsInternalNode(LoadVersionType v)
+  {
+    return !IsLeafNode(v);
+  }
+
+  static inline size_t
+  KeySlotsUsed(LoadVersionType v)
+  {
+    return (Load(v) & HDR_KEY_SLOTS_MASK) >> HDR_KEY_SLOTS_SHIFT;
+  }
+
+  static inline bool
+  IsLocked(LoadVersionType v)
+  {
+    return (Load(v) & HDR_LOCKED_MASK);
+  }
+
+  static inline bool
+  IsRoot(LoadVersionType v)
+  {
+    return (Load(v) & HDR_IS_ROOT_MASK);
+  }
+
+  static inline bool
+  IsModifying(LoadVersionType v)
+  {
+    return (Load(v) & HDR_MODIFYING_MASK);
+  }
+
+  static inline bool
+  IsDeleting(LoadVersionType v)
+  {
+    return (Load(v) & HDR_DELETING_MASK);
+  }
+
+  static inline uint64_t
+  Version(LoadVersionType v)
+  {
+    return (Load(v) & HDR_VERSION_MASK) >> HDR_VERSION_SHIFT;
+  }
+
+  static std::string
+  VersionInfoStr(LoadVersionType v)
+  {
+    std::ostringstream buf;
+    buf << "[";
+
+    if (IsLeafNode(v))
+      buf << "LEAF";
+    else
+      buf << "INT";
+    buf << " | ";
+
+    buf << KeySlotsUsed(v) << " | ";
+
+    if (IsLocked(v))
+      buf << "LOCKED";
+    else
+      buf << "-";
+    buf << " | ";
+
+    if (IsRoot(v))
+      buf << "ROOT";
+    else
+      buf << "-";
+    buf << " | ";
+
+    if (IsModifying(v))
+      buf << "MOD";
+    else
+      buf << "-";
+    buf << " | ";
+
+    if (IsDeleting(v))
+      buf << "DEL";
+    else
+      buf << "-";
+    buf << " | ";
+
+    buf << Version(v);
+
+    buf << "]";
+    return buf.str();
+  }
+
+  // mutators
+
+  static inline void
+  SetKeySlotsUsed(VersionType &v, size_t n)
+  {
+    INVARIANT(n <= NKeysPerNode);
+    INVARIANT(IsModifying(v));
+    uint64_t h = Load(v);
+    h &= ~HDR_KEY_SLOTS_MASK;
+    h |= (n << HDR_KEY_SLOTS_SHIFT);
+    Store(v, h);
+  }
+
+  static inline void
+  IncKeySlotsUsed(VersionType &v)
+  {
+    SetKeySlotsUsed(v, KeySlotsUsed(v) + 1);
+  }
+
+  static inline void
+  DecKeySlotsUsed(VersionType &v)
+  {
+    INVARIANT(KeySlotsUsed(v) > 0);
+    SetKeySlotsUsed(v, KeySlotsUsed(v) - 1);
+  }
+
+  static inline void
+  SetRoot(VersionType &v)
+  {
+    INVARIANT(IsLocked(v));
+    INVARIANT(!IsRoot(v));
+    uint64_t h = Load(v);
+    h |= HDR_IS_ROOT_MASK;
+    Store(v, h);
+  }
+
+  static inline void
+  ClearRoot(VersionType &v)
+  {
+    INVARIANT(IsLocked(v));
+    INVARIANT(IsRoot(v));
+    uint64_t h = Load(v);
+    h &= ~HDR_IS_ROOT_MASK;
+    Store(v, h);
+  }
+
+  static inline void
+  MarkModifying(VersionType &v)
+  {
+    INVARIANT(IsLocked(v));
+    INVARIANT(!IsModifying(v));
+    uint64_t h = Load(v);
+    h |= HDR_MODIFYING_MASK;
+    Store(v, h);
+  }
+
+  static inline void
+  MarkDeleting(VersionType &v)
+  {
+    INVARIANT(IsLocked(v));
+    INVARIANT(!IsDeleting(v));
+    uint64_t h = Load(v);
+    h |= HDR_DELETING_MASK;
+    Store(v, h);
+  }
+
+  // concurrency control
+
+  static inline uint64_t
+  StableVersion(LoadVersionType v)
+  {
+    return U64Manip::StableVersion(v);
+  }
+
+  static inline uint64_t
+  UnstableVersion(LoadVersionType v)
+  {
+    return Load(v);
+  }
+
+  static inline bool
+  CheckVersion(LoadVersionType v, uint64_t stablev)
+  {
+    return U64Manip::CheckVersion(Load(v), stablev);
+  }
+
+  static inline uint64_t
+  Lock(VersionType &v)
+  {
+    return U64Manip::Lock(v);
+  }
+
+  static inline uint64_t
+  LockWithSpinCount(VersionType &v, unsigned &spins)
+  {
+    return U64Manip::LockWithSpinCount(v, spins);
+  }
+
+  static inline void
+  Unlock(VersionType &v)
+  {
+    U64Manip::Unlock(v);
+  }
+};
+
+struct base_btree_config {
+  static const unsigned int NKeysPerNode = 15;
+  static const bool RcuRespCaller = true;
+};
+
+struct concurrent_btree_traits : public base_btree_config {
+  typedef std::atomic<uint64_t> VersionType;
+};
+
+struct single_threaded_btree_traits : public base_btree_config {
+  typedef uint64_t VersionType;
+};
+
+/**
+ * A concurrent, variable key length b+-tree, optimized for read heavy
+ * workloads.
+ *
+ * This b+-tree maps uninterpreted binary strings (key_type) of arbitrary
+ * length to a single pointer (value_type). Binary string values are copied
+ * into the b+-tree, so the caller does not have to worry about preserving
+ * memory for key values.
+ *
+ * This b+-tree does not manage the memory pointed to by value_type. The
+ * pointer is treated completely opaquely.
+ *
+ * So far, this b+-tree has only been tested on 64-bit intel x86 processors.
+ * It's correctness, as it is implemented (not conceptually), requires the
+ * semantics of total store order (TSO) for correctness. To fix this, we would
+ * change compiler fences into actual memory fences, at the very least.
+ */
+template <typename P>
+class btree {
+  template <template <typename> class, typename>
+    friend class base_txn_btree;
+public:
+  typedef varkey key_type;
+  typedef std::string string_type;
+  typedef uint64_t key_slice;
+  typedef uint8_t* value_type;
+  typedef typename std::conditional<!P::RcuRespCaller,
+      scoped_rcu_region,
+      disabled_rcu_region>::type rcu_region;
+
+  // public to assist in testing
+  static const unsigned int NKeysPerNode    = P::NKeysPerNode;
+  static const unsigned int NMinKeysPerNode = P::NKeysPerNode / 2;
+
+private:
+
+  typedef std::pair<ssize_t, size_t> key_search_ret;
+
+  typedef
+    btree_version_manip<typename P::VersionType, NKeysPerNode>
+    VersionManip;
+  typedef
+    btree_version_manip<uint64_t, NKeysPerNode>
+    RawVersionManip;
+
+  struct node {
+
+    typename P::VersionType hdr_;
+
+#ifdef BTREE_LOCK_OWNERSHIP_CHECKING
+    std::thread::id lock_owner_;
+#endif /* BTREE_LOCK_OWNERSHIP_CHECKING */
+
+    /**
+     * Keys are assumed to be stored in contiguous sorted order, so that all
+     * the used slots are grouped together. That is, elems in positions
+     * [0, key_slots_used) are valid, and elems in positions
+     * [key_slots_used, NKeysPerNode) are empty
+     */
+    key_slice keys_[NKeysPerNode];
+
+    node() :
+      hdr_()
+#ifdef BTREE_LOCK_OWNERSHIP_CHECKING
+      , lock_owner_()
+#endif
+    {}
+    ~node()
+    {
+      INVARIANT(!is_locked());
+      INVARIANT(is_deleting());
+    }
+
+    inline bool
+    is_leaf_node() const
+    {
+      return VersionManip::IsLeafNode(hdr_);
+    }
+
+    inline bool
+    is_internal_node() const
+    {
+      return !is_leaf_node();
+    }
+
+    inline size_t
+    key_slots_used() const
+    {
+      return VersionManip::KeySlotsUsed(hdr_);
+    }
+
+    inline void
+    set_key_slots_used(size_t n)
+    {
+      VersionManip::SetKeySlotsUsed(hdr_, n);
+    }
+
+    inline void
+    inc_key_slots_used()
+    {
+      VersionManip::IncKeySlotsUsed(hdr_);
+    }
+
+    inline void
+    dec_key_slots_used()
+    {
+      VersionManip::DecKeySlotsUsed(hdr_);
+    }
+
+    inline bool
+    is_locked() const
+    {
+      return VersionManip::IsLocked(hdr_);
+    }
+
+#ifdef BTREE_LOCK_OWNERSHIP_CHECKING
+    inline bool
+    is_lock_owner() const
+    {
+      return std::this_thread::get_id() == lock_owner_;
+    }
+#else
+    inline bool
+    is_lock_owner() const
+    {
+      return true;
+    }
+#endif /* BTREE_LOCK_OWNERSHIP_CHECKING */
+
+    inline uint64_t
+    lock()
+    {
+#ifdef ENABLE_EVENT_COUNTERS
+      static event_avg_counter
+        evt_avg_btree_leaf_node_lock_acquire_spins(
+            util::cxx_typename<btree<P>>::value() +
+            std::string("_avg_btree_leaf_node_lock_acquire_spins"));
+      static event_avg_counter
+        evt_avg_btree_internal_node_lock_acquire_spins(
+            util::cxx_typename<btree<P>>::value() +
+            std::string("_avg_btree_internal_node_lock_acquire_spins"));
+      unsigned spins;
+      const uint64_t ret = VersionManip::LockWithSpinCount(hdr_, spins);
+      if (is_leaf_node())
+        evt_avg_btree_leaf_node_lock_acquire_spins.offer(spins);
+      else
+        evt_avg_btree_internal_node_lock_acquire_spins.offer(spins);
+#else
+      const uint64_t ret = VersionManip::Lock(hdr_);
+#endif
+#ifdef BTREE_LOCK_OWNERSHIP_CHECKING
+      lock_owner_ = std::this_thread::get_id();
+      AddNodeToLockRegion(this);
+      INVARIANT(is_lock_owner());
+#endif
+      return ret;
+    }
+
+    inline void
+    unlock()
+    {
+#ifdef BTREE_LOCK_OWNERSHIP_CHECKING
+      lock_owner_ = std::thread::id();
+      INVARIANT(!is_lock_owner());
+#endif
+      VersionManip::Unlock(hdr_);
+    }
+
+    inline bool
+    is_root() const
+    {
+      return VersionManip::IsRoot(hdr_);
+    }
+
+    inline void
+    set_root()
+    {
+      VersionManip::SetRoot(hdr_);
+    }
+
+    inline void
+    clear_root()
+    {
+      VersionManip::ClearRoot(hdr_);
+    }
+
+    inline bool
+    is_modifying() const
+    {
+      return VersionManip::IsModifying(hdr_);
+    }
+
+    inline void
+    mark_modifying()
+    {
+      VersionManip::MarkModifying(hdr_);
+    }
+
+    inline bool
+    is_deleting() const
+    {
+      return VersionManip::IsDeleting(hdr_);
+    }
+
+    inline void
+    mark_deleting()
+    {
+      VersionManip::MarkDeleting(hdr_);
+    }
+
+    inline uint64_t
+    unstable_version() const
+    {
+      return VersionManip::UnstableVersion(hdr_);
+    }
+
+    /**
+     * spin until we get a version which is not modifying (but can be locked)
+     */
+    inline uint64_t
+    stable_version() const
+    {
+      return VersionManip::StableVersion(hdr_);
+    }
+
+    inline bool
+    check_version(uint64_t version) const
+    {
+      return VersionManip::CheckVersion(hdr_, version);
+    }
+
+    inline std::string
+    version_info_str() const
+    {
+      return VersionManip::VersionInfoStr(hdr_);
+    }
+
+    void base_invariant_unique_keys_check() const;
+
+    // [min_key, max_key)
+    void
+    base_invariant_checker(const key_slice *min_key,
+                           const key_slice *max_key,
+                           bool is_root) const;
+
+    /** manually simulated virtual function (so we don't make node virtual) */
+    void
+    invariant_checker(const key_slice *min_key,
+                      const key_slice *max_key,
+                      const node *left_sibling,
+                      const node *right_sibling,
+                      bool is_root) const;
+
+    /** another manually simulated virtual function */
+    inline void prefetch() const;
+  };
+
+  struct leaf_node : public node {
+    union value_or_node_ptr {
+      value_type v_;
+      node *n_;
+    };
+
+    key_slice min_key_; // really is min_key's key slice
+    value_or_node_ptr values_[NKeysPerNode];
+
+    // format is:
+    // [ slice_length | type | unused ]
+    // [    0:4       |  4:5 |  5:8   ]
+    uint8_t lengths_[NKeysPerNode];
+
+    leaf_node *prev_;
+    leaf_node *next_;
+
+    // starts out empty- once set, doesn't get freed until dtor (even if all
+    // keys w/ suffixes get removed)
+    imstring *suffixes_;
+
+    inline ALWAYS_INLINE varkey
+    suffix(size_t i) const
+    {
+      return suffixes_ ? varkey(suffixes_[i]) : varkey();
+    }
+
+    //static event_counter g_evt_suffixes_array_created;
+
+    inline void
+    alloc_suffixes()
+    {
+      INVARIANT(this->is_modifying());
+      INVARIANT(!suffixes_);
+      suffixes_ = new imstring[NKeysPerNode];
+      //++g_evt_suffixes_array_created;
+    }
+
+    inline void
+    ensure_suffixes()
+    {
+      INVARIANT(this->is_modifying());
+      if (!suffixes_)
+        alloc_suffixes();
+    }
+
+    leaf_node();
+    ~leaf_node();
+
+    static const uint64_t LEN_LEN_MASK = 0xf;
+
+    static const uint64_t LEN_TYPE_SHIFT = 4;
+    static const uint64_t LEN_TYPE_MASK = 0x1 << LEN_TYPE_SHIFT;
+
+    inline void
+    prefetch() const
+    {
+#ifdef BTREE_NODE_PREFETCH
+      prefetch_object(this);
+#endif
+    }
+
+    inline size_t
+    keyslice_length(size_t n) const
+    {
+      INVARIANT(n < NKeysPerNode);
+      return lengths_[n] & LEN_LEN_MASK;
+    }
+
+    inline void
+    keyslice_set_length(size_t n, size_t len, bool layer)
+    {
+      INVARIANT(n < NKeysPerNode);
+      INVARIANT(this->is_modifying());
+      INVARIANT(len <= 9);
+      INVARIANT(!layer || len == 9);
+      lengths_[n] = (len | (layer ? LEN_TYPE_MASK : 0));
+    }
+
+    inline bool
+    value_is_layer(size_t n) const
+    {
+      INVARIANT(n < NKeysPerNode);
+      return lengths_[n] & LEN_TYPE_MASK;
+    }
+
+    inline void
+    value_set_layer(size_t n)
+    {
+      INVARIANT(n < NKeysPerNode);
+      INVARIANT(this->is_modifying());
+      INVARIANT(keyslice_length(n) == 9);
+      INVARIANT(!value_is_layer(n));
+      lengths_[n] |= LEN_TYPE_MASK;
+    }
+
+    /**
+     * keys[key_search(k).first] == k if key_search(k).first != -1
+     * key does not exist otherwise. considers key length also
+     */
+    inline key_search_ret
+    key_search(key_slice k, size_t len) const
+    {
+      size_t n = this->key_slots_used();
+      ssize_t lower = 0;
+      ssize_t upper = n;
+      while (lower < upper) {
+        ssize_t i = (lower + upper) / 2;
+        key_slice k0 = this->keys_[i];
+        size_t len0 = this->keyslice_length(i);
+        if (k0 < k || (k0 == k && len0 < len))
+          lower = i + 1;
+        else if (k0 == k && len0 == len)
+          return key_search_ret(i, n);
+        else
+          upper = i;
+      }
+      return key_search_ret(-1, n);
+    }
+
+    /**
+     * tightest lower bound key, -1 if no such key exists. operates only
+     * on key slices (internal nodes have unique key slices)
+     */
+    inline key_search_ret
+    key_lower_bound_search(key_slice k, size_t len) const
+    {
+      ssize_t ret = -1;
+      size_t n = this->key_slots_used();
+      ssize_t lower = 0;
+      ssize_t upper = n;
+      while (lower < upper) {
+        ssize_t i = (lower + upper) / 2;
+        key_slice k0 = this->keys_[i];
+        size_t len0 = this->keyslice_length(i);
+        if (k0 < k || (k0 == k && len0 < len)) {
+          ret = i;
+          lower = i + 1;
+        } else if (k0 == k && len0 == len) {
+          return key_search_ret(i, n);
+        } else {
+          upper = i;
+        }
+      }
+      return key_search_ret(ret, n);
+    }
+
+    void
+    invariant_checker_impl(const key_slice *min_key,
+                           const key_slice *max_key,
+                           const node *left_sibling,
+                           const node *right_sibling,
+                           bool is_root) const;
+
+    static inline leaf_node*
+    alloc()
+    {
+      void * const p = rcu::s_instance.alloc(LeafNodeAllocSize);
+      INVARIANT(p);
+      return new (p) leaf_node;
+    }
+
+    static void
+    deleter(void *p)
+    {
+      leaf_node *n = (leaf_node *) p;
+      INVARIANT(n->is_deleting());
+      INVARIANT(!n->is_locked());
+      n->~leaf_node();
+      rcu::s_instance.dealloc(p, LeafNodeAllocSize);
+    }
+
+    static inline void
+    release(leaf_node *n)
+    {
+      if (unlikely(!n))
+        return;
+      n->mark_deleting();
+      rcu::s_instance.free_with_fn(n, deleter);
+    }
+
+  };
+
+  struct internal_node : public node {
+    /**
+     * child at position child_idx is responsible for keys
+     * [keys[child_idx - 1], keys[child_idx])
+     *
+     * in the case where child_idx == 0 or child_idx == this->key_slots_used(), then
+     * the responsiblity value of the min/max key, respectively, is determined
+     * by the parent
+     */
+    node *children_[NKeysPerNode + 1];
+
+    internal_node();
+    ~internal_node();
+
+    inline void
+    prefetch() const
+    {
+#ifdef BTREE_NODE_PREFETCH
+      prefetch_object(this);
+#endif
+    }
+
+    /**
+     * keys[key_search(k).first] == k if key_search(k).first != -1
+     * key does not exist otherwise. operates ony on key slices
+     * (internal nodes have unique key slices)
+     */
+    inline key_search_ret
+    key_search(key_slice k) const
+    {
+      size_t n = this->key_slots_used();
+      ssize_t lower = 0;
+      ssize_t upper = n;
+      while (lower < upper) {
+        ssize_t i = (lower + upper) / 2;
+        key_slice k0 = this->keys_[i];
+        if (k0 == k)
+          return key_search_ret(i, n);
+        else if (k0 > k)
+          upper = i;
+        else
+          lower = i + 1;
+      }
+      return key_search_ret(-1, n);
+    }
+
+    /**
+     * tightest lower bound key, -1 if no such key exists. operates only
+     * on key slices (internal nodes have unique key slices)
+     */
+    inline key_search_ret
+    key_lower_bound_search(key_slice k) const
+    {
+      ssize_t ret = -1;
+      size_t n = this->key_slots_used();
+      ssize_t lower = 0;
+      ssize_t upper = n;
+      while (lower < upper) {
+        ssize_t i = (lower + upper) / 2;
+        key_slice k0 = this->keys_[i];
+        if (k0 == k)
+          return key_search_ret(i, n);
+        else if (k0 > k)
+          upper = i;
+        else {
+          ret = i;
+          lower = i + 1;
+        }
+      }
+      return key_search_ret(ret, n);
+    }
+
+    void
+    invariant_checker_impl(const key_slice *min_key,
+                           const key_slice *max_key,
+                           const node *left_sibling,
+                           const node *right_sibling,
+                           bool is_root) const;
+
+    // XXX: alloc(), deleter(), and release() are copied from leaf_node-
+    // we should templatize them to avoid code duplication
+
+    static inline internal_node*
+    alloc()
+    {
+      void * const p = rcu::s_instance.alloc(InternalNodeAllocSize);
+      INVARIANT(p);
+      return new (p) internal_node;
+    }
+
+    static void
+    deleter(void *p)
+    {
+      internal_node *n = (internal_node *) p;
+      INVARIANT(n->is_deleting());
+      INVARIANT(!n->is_locked());
+      n->~internal_node();
+      rcu::s_instance.dealloc(p, InternalNodeAllocSize);
+    }
+
+    static inline void
+    release(internal_node *n)
+    {
+      if (unlikely(!n))
+        return;
+      n->mark_deleting();
+      rcu::s_instance.free_with_fn(n, deleter);
+    }
+
+  } PACKED;
+
+#ifdef BTREE_LOCK_OWNERSHIP_CHECKING
+public:
+  static inline void
+  NodeLockRegionBegin()
+  {
+    ownership_checker<btree<P>, typename btree<P>::node>::NodeLockRegionBegin();
+  }
+  static inline void
+  AssertAllNodeLocksReleased()
+  {
+    ownership_checker<btree<P>, typename btree<P>::node>::AssertAllNodeLocksReleased();
+  }
+private:
+  static inline void
+  AddNodeToLockRegion(const node *n)
+  {
+    ownership_checker<btree<P>, typename btree<P>::node>::AddNodeToLockRegion(n);
+  }
+#endif
+
+#ifdef BTREE_NODE_ALLOC_CACHE_ALIGNED
+  static const size_t LeafNodeAllocSize = util::round_up<size_t, LG_CACHELINE_SIZE>(sizeof(leaf_node));
+  static const size_t InternalNodeAllocSize = util::round_up<size_t, LG_CACHELINE_SIZE>(sizeof(internal_node));
+#else
+  static const size_t LeafNodeAllocSize = sizeof(leaf_node);
+  static const size_t InternalNodeAllocSize = sizeof(internal_node);
+#endif
+
+  static inline leaf_node*
+  AsLeaf(node *n)
+  {
+    INVARIANT(!n || n->is_leaf_node());
+    return static_cast<leaf_node *>(n);
+  }
+
+  static inline const leaf_node*
+  AsLeaf(const node *n)
+  {
+    return AsLeaf(const_cast<node *>(n));
+  }
+
+  static inline internal_node*
+  AsInternal(node *n)
+  {
+    INVARIANT(!n || n->is_internal_node());
+    return static_cast<internal_node *>(n);
+  }
+
+  static inline const internal_node*
+  AsInternal(const node *n)
+  {
+    return AsInternal(const_cast<node *>(n));
+  }
+
+  static inline leaf_node *
+  AsLeafCheck(node *n, uint64_t v)
+  {
+    return likely(n) && RawVersionManip::IsLeafNode(v) ?
+      static_cast<leaf_node *>(n) : NULL;
+  }
+
+  static inline leaf_node*
+  AsLeafCheck(node *n)
+  {
+    return likely(n) && n->is_leaf_node() ?
+      static_cast<leaf_node *>(n) : NULL;
+  }
+
+  static inline const leaf_node*
+  AsLeafCheck(const node *n, uint64_t v)
+  {
+    return AsLeafCheck(const_cast<node *>(n), v);
+  }
+
+  static inline const leaf_node*
+  AsLeafCheck(const node *n)
+  {
+    return AsLeafCheck(const_cast<node *>(n));
+  }
+
+  static inline internal_node*
+  AsInternalCheck(node *n)
+  {
+    return likely(n) && n->is_internal_node() ? static_cast<internal_node *>(n) : NULL;
+  }
+
+  static inline const internal_node*
+  AsInternalCheck(const node *n)
+  {
+    return AsInternalCheck(const_cast<node *>(n));
+  }
+
+  static inline void
+  UnlockNodes(typename util::vec<node *>::type &locked_nodes)
+  {
+    for (auto it = locked_nodes.begin(); it != locked_nodes.end(); ++it)
+      (*it)->unlock();
+    locked_nodes.clear();
+  }
+
+  template <typename T>
+  static inline T
+  UnlockAndReturn(typename util::vec<node *>::type &locked_nodes, T t)
+  {
+    UnlockNodes(locked_nodes);
+    return t;
+  }
+
+  static bool
+  CheckVersion(uint64_t a, uint64_t b)
+  {
+    return VersionManip::CheckVersion(a, b);
+  }
+
+  /**
+   * Is not thread safe, and does not use RCU to free memory
+   *
+   * Should only be called when there are no outstanding operations on
+   * any nodes reachable from n
+   */
+  static void recursive_delete(node *n);
+
+  node *volatile root_;
+
+public:
+
+  // XXX(stephentu): trying out a very opaque node API for now
+  typedef struct node node_opaque_t;
+  typedef std::pair< const node_opaque_t *, uint64_t > versioned_node_t;
+  struct insert_info_t {
+    const node_opaque_t* node;
+    uint64_t old_version;
+    uint64_t new_version;
+  };
+
+  btree() : root_(leaf_node::alloc())
+  {
+    static_assert(
+        NKeysPerNode > (sizeof(key_slice) + 2), "XX"); // so we can always do a split
+    static_assert(
+        NKeysPerNode <=
+        (VersionManip::HDR_KEY_SLOTS_MASK >> VersionManip::HDR_KEY_SLOTS_SHIFT), "XX");
+
+#ifdef CHECK_INVARIANTS
+    root_->lock();
+    root_->set_root();
+    root_->unlock();
+#else
+    root_->set_root();
+#endif /* CHECK_INVARIANTS */
+  }
+
+  ~btree()
+  {
+    // NOTE: it is assumed on deletion time there are no
+    // outstanding requests to the btree, so deletion proceeds
+    // in a non-threadsafe manner
+    recursive_delete(root_);
+    root_ = NULL;
+  }
+
+  /**
+   * NOT THREAD SAFE
+   */
+  inline void
+  clear()
+  {
+    recursive_delete(root_);
+    root_ = leaf_node::alloc();
+#ifdef CHECK_INVARIANTS
+    root_->lock();
+    root_->set_root();
+    root_->unlock();
+#else
+    root_->set_root();
+#endif /* CHECK_INVARIANTS */
+  }
+
+  /** Note: invariant checking is not thread safe */
+  inline void
+  invariant_checker() const
+  {
+    root_->invariant_checker(NULL, NULL, NULL, NULL, true);
+  }
+
+          /** NOTE: the public interface assumes that the caller has taken care
+           * of setting up RCU */
+
+  inline bool
+  search(const key_type &k, value_type &v,
+         versioned_node_t *search_info = nullptr) const
+  {
+    rcu_region guard;
+    typename util::vec<leaf_node *>::type ns;
+    return search_impl(k, v, ns, search_info);
+  }
+
+  /**
+   * The low level callback interface is as follows:
+   *
+   * Consider a scan in the range [a, b):
+   *   1) on_resp_node() is called at least once per node which
+   *      has a responibility range that overlaps with the scan range
+   *   2) invoke() is called per <k, v>-pair such that k is in [a, b)
+   *
+   * The order of calling on_resp_node() and invoke() is up to the implementation.
+   */
+  class low_level_search_range_callback {
+  public:
+    virtual ~low_level_search_range_callback() {}
+
+    /**
+     * This node lies within the search range (at version v)
+     */
+    virtual void on_resp_node(const node_opaque_t *n, uint64_t version) = 0;
+
+    /**
+     * This key/value pair was read from node n @ version
+     */
+    virtual bool invoke(const string_type &k, value_type v,
+                        const node_opaque_t *n, uint64_t version) = 0;
+  };
+
+  /**
+   * A higher level interface if you don't care about node and version numbers
+   */
+  class search_range_callback : public low_level_search_range_callback {
+  public:
+    virtual void
+    on_resp_node(const node_opaque_t *n, uint64_t version)
+    {
+    }
+
+    virtual bool
+    invoke(const string_type &k, value_type v,
+           const node_opaque_t *n, uint64_t version)
+    {
+      return invoke(k, v);
+    }
+
+    virtual bool invoke(const string_type &k, value_type v) = 0;
+  };
+
+private:
+  template <typename T>
+  class type_callback_wrapper : public search_range_callback {
+  public:
+    type_callback_wrapper(T *callback) : callback_(callback) {}
+    virtual bool
+    invoke(const string_type &k, value_type v)
+    {
+      return callback_->operator()(k, v);
+    }
+  private:
+    T *const callback_;
+  };
+
+  struct leaf_kvinfo {
+    key_slice key_; // in host endian
+    key_slice key_big_endian_;
+    typename leaf_node::value_or_node_ptr vn_;
+    bool layer_;
+    size_t length_;
+    varkey suffix_;
+    leaf_kvinfo() {} // for STL
+    leaf_kvinfo(key_slice key,
+                typename leaf_node::value_or_node_ptr vn,
+                bool layer,
+                size_t length,
+                const varkey &suffix)
+      : key_(key), key_big_endian_(util::big_endian_trfm<key_slice>()(key)),
+        vn_(vn), layer_(layer), length_(length), suffix_(suffix)
+    {}
+
+    inline const char *
+    keyslice() const
+    {
+      return (const char *) &key_big_endian_;
+    }
+  };
+
+  bool search_range_at_layer(leaf_node *leaf,
+                             string_type &prefix,
+                             const key_type &lower,
+                             bool inc_lower,
+                             const key_type *upper,
+                             low_level_search_range_callback &callback) const;
+
+public:
+
+  /**
+   * For all keys in [lower, *upper), invoke callback in ascending order.
+   * If upper is NULL, then there is no upper bound
+   *
+
+   * This function by default provides a weakly consistent view of the b-tree. For
+   * instance, consider the following tree, where n = 3 is the max number of
+   * keys in a node:
+   *
+   *              [D|G]
+   *             /  |  \
+   *            /   |   \
+   *           /    |    \
+   *          /     |     \
+   *   [A|B|C]<->[D|E|F]<->[G|H|I]
+   *
+   * Suppose we want to scan [A, inf), so we traverse to the leftmost leaf node
+   * and start a left-to-right walk. Suppose we have emitted keys A, B, and C,
+   * and we are now just about to scan the middle leaf node.  Now suppose
+   * another thread concurrently does delete(A), followed by a delete(H).  Now
+   * the scaning thread resumes and emits keys D, E, F, G, and I, omitting H
+   * because H was deleted. This is an inconsistent view of the b-tree, since
+   * the scanning thread has observed the deletion of H but did not observe the
+   * deletion of A, but we know that delete(A) happens before delete(H).
+   *
+   * The weakly consistent guarantee provided is the following: all keys
+   * which, at the time of invocation, are known to exist in the btree
+   * will be discovered on a scan (provided the key falls within the scan's range),
+   * and provided there are no concurrent modifications/removals of that key
+   *
+   * Note that scans within a single node are consistent
+   *
+   * XXX: add other modes which provide better consistency:
+   * A) locking mode
+   * B) optimistic validation mode
+   *
+   * the last string parameter is an optional string buffer to use:
+   * if null, a stack allocated string will be used. if not null, must
+   * ensure:
+   *   A) buf->empty() at the beginning
+   *   B) no concurrent mutation of string
+   * note that string contents upon return are arbitrary
+   */
+  void
+  search_range_call(const key_type &lower,
+                    const key_type *upper,
+                    low_level_search_range_callback &callback,
+                    string_type *buf = nullptr) const;
+
+  // (lower, upper]
+  void
+  rsearch_range_call(const key_type &upper,
+                     const key_type *lower,
+                     low_level_search_range_callback &callback,
+                     std::string *buf = nullptr) const
+  {
+    NDB_UNIMPLEMENTED("rsearch_range_call");
+  }
+
+  /**
+   * Callback is expected to implement bool operator()(key_slice k, value_type v),
+   * where the callback returns true if it wants to keep going, false otherwise
+   *
+   */
+  template <typename T>
+  inline void
+  search_range(const key_type &lower,
+               const key_type *upper,
+               T &callback,
+               string_type *buf = nullptr) const
+  {
+    type_callback_wrapper<T> w(&callback);
+    search_range_call(lower, upper, w, buf);
+  }
+
+  template <typename F>
+  inline void
+  rsearch_range(const key_type &upper,
+                const key_type *lower,
+                F& callback,
+                std::string *buf = nullptr) const
+  {
+    NDB_UNIMPLEMENTED("rsearch_range");
+  }
+
+  /**
+   * returns true if key k did not already exist, false otherwise
+   * If k exists with a different mapping, still returns false
+   *
+   * If false and old_v is not NULL, then the overwritten value of v
+   * is written into old_v
+   */
+  inline bool
+  insert(const key_type &k, value_type v,
+         value_type *old_v = NULL,
+         insert_info_t *insert_info = NULL)
+  {
+    rcu_region guard;
+    return insert_stable_location((node **) &root_, k, v, false, old_v, insert_info);
+  }
+
+  /**
+   * Only puts k=>v if k does not exist in map. returns true
+   * if k inserted, false otherwise (k exists already)
+   */
+  inline bool
+  insert_if_absent(const key_type &k, value_type v,
+                   insert_info_t *insert_info = NULL)
+  {
+    rcu_region guard;
+    return insert_stable_location((node **) &root_, k, v, true, NULL, insert_info);
+  }
+
+  /**
+   * return true if a value was removed, false otherwise.
+   *
+   * if true and old_v is not NULL, then the removed value of v
+   * is written into old_v
+   */
+  inline bool
+  remove(const key_type &k, value_type *old_v = NULL)
+  {
+    rcu_region guard;
+    return remove_stable_location((node **) &root_, k, old_v);
+  }
+
+private:
+  bool
+  insert_stable_location(node **root_location, const key_type &k, value_type v,
+                         bool only_if_absent, value_type *old_v,
+                         insert_info_t *insert_info);
+
+  bool
+  remove_stable_location(node **root_location, const key_type &k, value_type *old_v);
+
+public:
+
+  /**
+   * The tree walk API is a bit strange, due to the optimistic nature of the
+   * btree.
+   *
+   * The way it works is that, on_node_begin() is first called. In
+   * on_node_begin(), a callback function should read (but not modify) the
+   * values it is interested in, and save them.
+   *
+   * Then, either one of on_node_success() or on_node_failure() is called. If
+   * on_node_success() is called, then the previous values read in
+   * on_node_begin() are indeed valid.  If on_node_failure() is called, then
+   * the previous values are not valid and should be discarded.
+   */
+  class tree_walk_callback {
+  public:
+    virtual ~tree_walk_callback() {}
+    virtual void on_node_begin(const node_opaque_t *n) = 0;
+    virtual void on_node_success() = 0;
+    virtual void on_node_failure() = 0;
+  };
+
+  void tree_walk(tree_walk_callback &callback) const;
+
+private:
+  class size_walk_callback : public tree_walk_callback {
+  public:
+    size_walk_callback() : spec_size_(0), size_(0) {}
+    virtual void on_node_begin(const node_opaque_t *n);
+    virtual void on_node_success();
+    virtual void on_node_failure();
+    inline size_t get_size() const { return size_; }
+  private:
+    size_t spec_size_;
+    size_t size_;
+  };
+
+public:
+  /**
+   * Is thread-safe, but not really designed to perform well with concurrent
+   * modifications. also the value returned is not consistent given concurrent
+   * modifications
+   */
+  inline size_t
+  size() const
+  {
+    size_walk_callback c;
+    tree_walk(c);
+    return c.get_size();
+  }
+
+  static inline uint64_t
+  ExtractVersionNumber(const node_opaque_t *n)
+  {
+    // XXX(stephentu): I think we must use stable_version() for
+    // correctness, but I am not 100% sure. It's definitely correct to use it,
+    // but maybe we can get away with unstable_version()?
+    return RawVersionManip::Version(n->stable_version());
+  }
+
+  // [value, has_suffix]
+  static std::vector< std::pair<value_type, bool> >
+  ExtractValues(const node_opaque_t *n);
+
+  void print() {
+  }
+
+  /**
+   * Not well defined if n is being concurrently modified, just for debugging
+   */
+  static std::string
+  NodeStringify(const node_opaque_t *n);
+
+  static inline size_t
+  InternalNodeSize()
+  {
+    return sizeof(internal_node);
+  }
+
+  static inline size_t
+  LeafNodeSize()
+  {
+    return sizeof(leaf_node);
+  }
+
+private:
+
+  /**
+   * Move the array slice from [p, n) to the right by k position, occupying [p + k, n + k),
+   * leaving the values of array[p] to array[p + k -1] undefined. Has no effect if p >= n
+   *
+   * Note: Assumes that array[n] is valid memory.
+   */
+  template <typename T>
+  static inline ALWAYS_INLINE void
+  sift_right(T *array, size_t p, size_t n, size_t k = 1)
+  {
+    if (k == 0)
+      return;
+    for (size_t i = n + k - 1; i > p + k - 1; i--)
+      array[i] = array[i - k];
+  }
+
+  // use swap() to do moves, for efficiency- avoid this
+  // variant when using primitive arrays
+  template <typename T>
+  static inline ALWAYS_INLINE void
+  sift_swap_right(T *array, size_t p, size_t n, size_t k = 1)
+  {
+    if (k == 0)
+      return;
+    for (size_t i = n + k - 1; i > p + k - 1; i--)
+      array[i].swap(array[i - k]);
+  }
+
+  /**
+   * Move the array slice from [p + k, n) to the left by k positions, occupying [p, n - k),
+   * overwriting array[p]..array[p+k-1] Has no effect if p + k >= n
+   */
+  template <typename T>
+  static inline ALWAYS_INLINE void
+  sift_left(T *array, size_t p, size_t n, size_t k = 1)
+  {
+    if (unlikely(p + k >= n))
+      return;
+    for (size_t i = p; i < n - k; i++)
+      array[i] = array[i + k];
+  }
+
+  template <typename T>
+  static inline ALWAYS_INLINE void
+  sift_swap_left(T *array, size_t p, size_t n, size_t k = 1)
+  {
+    if (unlikely(p + k >= n))
+      return;
+    for (size_t i = p; i < n - k; i++)
+      array[i].swap(array[i + k]);
+  }
+
+  /**
+   * Copy [p, n) from source into dest. Has no effect if p >= n
+   */
+  template <typename T>
+  static inline ALWAYS_INLINE void
+  copy_into(T *dest, T *source, size_t p, size_t n)
+  {
+    for (size_t i = p; i < n; i++)
+      *dest++ = source[i];
+  }
+
+  template <typename T>
+  static inline ALWAYS_INLINE void
+  swap_with(T *dest, T *source, size_t p, size_t n)
+  {
+    for (size_t i = p; i < n; i++)
+      (dest++)->swap(source[i]);
+  }
+
+  leaf_node *leftmost_descend_layer(node *n) const;
+
+  /**
+   * Assumes RCU region scope is held
+   */
+  bool search_impl(const key_type &k, value_type &v,
+                   typename util::vec<leaf_node *>::type &leaf_nodes,
+                   versioned_node_t *search_info = nullptr) const;
+
+  static leaf_node *
+  FindRespLeafNode(
+      leaf_node *leaf, uint64_t kslice, uint64_t &version);
+
+  /**
+   * traverses the lower leaf levels for a leaf node resp for kslice such that
+   * version is stable and not deleting. resp info is given via idxmatch +
+   * idxlowerbound
+   *
+   * if idxmatch != -1, then ignore idxlowerbound
+   *
+   * note to actually use the info, you still need to validate it (the info is
+   * tentative as of the version)
+   */
+  static leaf_node *
+  FindRespLeafLowerBound(
+      leaf_node *leaf, uint64_t kslice,
+      size_t kslicelen, uint64_t &version,
+      size_t &n, ssize_t &idxmatch, ssize_t &idxlowerbound);
+
+  static leaf_node *
+  FindRespLeafExact(
+      leaf_node *leaf, uint64_t kslice,
+      size_t kslicelen, uint64_t &version,
+      size_t &n, ssize_t &idxmatch);
+
+  typedef std::pair<node *, uint64_t> insert_parent_entry;
+
+  enum insert_status {
+    I_NONE_NOMOD, // no nodes split nor modified
+    I_NONE_MOD, // no nodes split, but modified
+    I_RETRY,
+    I_SPLIT, // node(s) split
+  };
+
+  /**
+   * insert k=>v into node n. if this insert into n causes it to split into two
+   * nodes, return the new node (upper half of keys). in this case, min_key is set to the
+   * smallest key that the new node is responsible for. otherwise return null, in which
+   * case min_key's value is not defined.
+   *
+   * NOTE: our implementation of insert0() is not as efficient as possible, in favor
+   * of code clarity
+   */
+  insert_status
+  insert0(node *n,
+          const key_type &k,
+          value_type v,
+          bool only_if_absent,
+          value_type *old_v,
+          insert_info_t *insert_info,
+          key_slice &min_key,
+          node *&new_node,
+          typename util::vec<insert_parent_entry>::type &parents,
+          typename util::vec<node *>::type &locked_nodes);
+
+  enum remove_status {
+    R_NONE_NOMOD,
+    R_NONE_MOD,
+    R_RETRY,
+    R_STOLE_FROM_LEFT,
+    R_STOLE_FROM_RIGHT,
+    R_MERGE_WITH_LEFT,
+    R_MERGE_WITH_RIGHT,
+    R_REPLACE_NODE,
+  };
+
+  inline ALWAYS_INLINE void
+  remove_pos_from_leaf_node(leaf_node *leaf, size_t pos, size_t n)
+  {
+    INVARIANT(leaf->key_slots_used() == n);
+    INVARIANT(pos < n);
+    if (leaf->value_is_layer(pos)) {
+#ifdef CHECK_INVARIANTS
+      leaf->values_[pos].n_->lock();
+#endif
+      leaf->values_[pos].n_->mark_deleting();
+      INVARIANT(leaf->values_[pos].n_->is_leaf_node());
+      INVARIANT(leaf->values_[pos].n_->key_slots_used() == 0);
+      leaf_node::release((leaf_node *) leaf->values_[pos].n_);
+#ifdef CHECK_INVARIANTS
+      leaf->values_[pos].n_->unlock();
+#endif
+    }
+    sift_left(leaf->keys_, pos, n);
+    sift_left(leaf->values_, pos, n);
+    sift_left(leaf->lengths_, pos, n);
+    if (leaf->suffixes_)
+      sift_swap_left(leaf->suffixes_, pos, n);
+    leaf->dec_key_slots_used();
+  }
+
+  inline ALWAYS_INLINE void
+  remove_pos_from_internal_node(
+      internal_node *internal, size_t key_pos, size_t child_pos, size_t n)
+  {
+    INVARIANT(internal->key_slots_used() == n);
+    INVARIANT(key_pos < n);
+    INVARIANT(child_pos < n + 1);
+    sift_left(internal->keys_, key_pos, n);
+    sift_left(internal->children_, child_pos, n + 1);
+    internal->dec_key_slots_used();
+  }
+
+  struct remove_parent_entry {
+    // non-const members for STL
+    node *parent_;
+    node *parent_left_sibling_;
+    node *parent_right_sibling_;
+    uint64_t parent_version_;
+
+    // default ctor for STL
+    remove_parent_entry()
+      : parent_(NULL), parent_left_sibling_(NULL),
+        parent_right_sibling_(NULL), parent_version_(0)
+    {}
+
+    remove_parent_entry(node *parent,
+                        node *parent_left_sibling,
+                        node *parent_right_sibling,
+                        uint64_t parent_version)
+      : parent_(parent), parent_left_sibling_(parent_left_sibling),
+        parent_right_sibling_(parent_right_sibling),
+        parent_version_(parent_version)
+    {}
+  };
+
+  remove_status
+  remove0(node *np,
+          key_slice *min_key,
+          key_slice *max_key,
+          const key_type &k,
+          value_type *old_v,
+          node *left_node,
+          node *right_node,
+          key_slice &new_key,
+          node *&replace_node,
+          typename util::vec<remove_parent_entry>::type &parents,
+          typename util::vec<node *>::type &locked_nodes);
+};
+
+template <typename P>
+inline void
+btree<P>::node::prefetch() const
+{
+  if (is_leaf_node())
+    AsLeaf(this)->prefetch();
+  else
+    AsInternal(this)->prefetch();
+}
+
+extern void TestConcurrentBtreeFast();
+extern void TestConcurrentBtreeSlow();
+
+#if !NDB_MASSTREE
+typedef btree<concurrent_btree_traits> concurrent_btree;
+typedef btree<single_threaded_btree_traits> single_threaded_btree;
+#endif
diff --git a/silo/btree_choice.h b/silo/btree_choice.h
new file mode 100644 (file)
index 0000000..e37ad27
--- /dev/null
@@ -0,0 +1,8 @@
+#pragma once
+
+#if NDB_MASSTREE
+#include "masstree_btree.h"
+#else
+#include "btree.h"
+#include "btree_impl.h"
+#endif
diff --git a/silo/btree_impl.h b/silo/btree_impl.h
new file mode 100644 (file)
index 0000000..0595b90
--- /dev/null
@@ -0,0 +1,2042 @@
+#pragma once
+
+#include <unistd.h>
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <stack>
+#include <vector>
+#include <sstream>
+#include <atomic>
+#include <memory>
+
+#include "core.h"
+#include "btree.h"
+#include "thread.h"
+#include "txn.h"
+#include "util.h"
+#include "scopedperf.hh"
+
+template <typename P>
+void
+btree<P>::node::base_invariant_unique_keys_check() const
+{
+  size_t n = this->key_slots_used();
+  if (n == 0)
+    return;
+  if (is_leaf_node()) {
+    const leaf_node *leaf = AsLeaf(this);
+    typedef std::pair<key_slice, size_t> leaf_key;
+    leaf_key prev;
+    prev.first = keys_[0];
+    prev.second = leaf->keyslice_length(0);
+    ALWAYS_ASSERT(prev.second <= 9);
+    ALWAYS_ASSERT(!leaf->is_layer(0) || prev.second == 9);
+    if (!leaf->is_layer(0) && prev.second == 9) {
+      ALWAYS_ASSERT(leaf->suffixes_);
+      ALWAYS_ASSERT(leaf->suffixes_[0].size() >= 1);
+    }
+    for (size_t i = 1; i < n; i++) {
+      leaf_key cur_key;
+      cur_key.first = keys_[i];
+      cur_key.second = leaf->keyslice_length(i);
+      ALWAYS_ASSERT(cur_key.second <= 9);
+      ALWAYS_ASSERT(!leaf->is_layer(i) || cur_key.second == 9);
+      if (!leaf->is_layer(i) && cur_key.second == 9) {
+        ALWAYS_ASSERT(leaf->suffixes_);
+        ALWAYS_ASSERT(leaf->suffixes_[i].size() >= 1);
+      }
+      ALWAYS_ASSERT(cur_key > prev);
+      prev = cur_key;
+    }
+  } else {
+    key_slice prev = keys_[0];
+    for (size_t i = 1; i < n; i++) {
+      ALWAYS_ASSERT(keys_[i] > prev);
+      prev = keys_[i];
+    }
+  }
+}
+
+template <typename P>
+void
+btree<P>::node::base_invariant_checker(const key_slice *min_key,
+                                       const key_slice *max_key,
+                                       bool is_root) const
+{
+  ALWAYS_ASSERT(!is_locked());
+  ALWAYS_ASSERT(!is_modifying());
+  ALWAYS_ASSERT(this->is_root() == is_root);
+  size_t n = this->key_slots_used();
+  ALWAYS_ASSERT(n <= NKeysPerNode);
+  if (is_root) {
+    if (is_internal_node())
+      ALWAYS_ASSERT(n >= 1);
+  } else {
+    if (is_internal_node())
+      ALWAYS_ASSERT(n >= NMinKeysPerNode);
+    else
+      // key-slices constrain splits
+      ALWAYS_ASSERT(n >= 1);
+  }
+  for (size_t i = 0; i < n; i++) {
+    ALWAYS_ASSERT(!min_key || keys_[i] >= *min_key);
+    ALWAYS_ASSERT(!max_key || keys_[i] < *max_key);
+  }
+  base_invariant_unique_keys_check();
+}
+
+template <typename P>
+void
+btree<P>::node::invariant_checker(const key_slice *min_key,
+                                  const key_slice *max_key,
+                                  const node *left_sibling,
+                                  const node *right_sibling,
+                                  bool is_root) const
+{
+  is_leaf_node() ?
+    AsLeaf(this)->invariant_checker_impl(min_key, max_key, left_sibling, right_sibling, is_root) :
+    AsInternal(this)->invariant_checker_impl(min_key, max_key, left_sibling, right_sibling, is_root) ;
+}
+
+//static event_counter evt_btree_leaf_node_creates("btree_leaf_node_creates");
+//static event_counter evt_btree_leaf_node_deletes("btree_leaf_node_deletes");
+
+//event_counter btree<P>::leaf_node::g_evt_suffixes_array_created("btree_leaf_node_suffix_array_creates");
+
+template <typename P>
+btree<P>::leaf_node::leaf_node()
+  : node(), min_key_(0), prev_(NULL), next_(NULL), suffixes_(NULL)
+{
+  //++evt_btree_leaf_node_creates;
+}
+
+template <typename P>
+btree<P>::leaf_node::~leaf_node()
+{
+  if (suffixes_)
+    delete [] suffixes_;
+  //suffixes_ = NULL;
+  //++evt_btree_leaf_node_deletes;
+}
+
+template <typename P>
+void
+btree<P>::leaf_node::invariant_checker_impl(const key_slice *min_key,
+                                            const key_slice *max_key,
+                                            const node *left_sibling,
+                                            const node *right_sibling,
+                                            bool is_root) const
+{
+  this->base_invariant_checker(min_key, max_key, is_root);
+  ALWAYS_ASSERT(!min_key || *min_key == this->min_key_);
+  ALWAYS_ASSERT(!is_root || min_key == NULL);
+  ALWAYS_ASSERT(!is_root || max_key == NULL);
+  ALWAYS_ASSERT(is_root || this->key_slots_used() > 0);
+  size_t n = this->key_slots_used();
+  for (size_t i = 0; i < n; i++)
+    if (this->is_layer(i))
+      this->values_[i].n_->invariant_checker(NULL, NULL, NULL, NULL, true);
+}
+
+//static event_counter evt_btree_internal_node_creates("btree_internal_node_creates");
+//static event_counter evt_btree_internal_node_deletes("btree_internal_node_deletes");
+
+template <typename P>
+btree<P>::internal_node::internal_node()
+{
+  VersionManip::Store(this->hdr_, 1);
+  //++evt_btree_internal_node_creates;
+}
+
+template <typename P>
+btree<P>::internal_node::~internal_node()
+{
+  //++evt_btree_internal_node_deletes;
+}
+
+template <typename P>
+void
+btree<P>::internal_node::invariant_checker_impl(const key_slice *min_key,
+                                                const key_slice *max_key,
+                                                const node *left_sibling,
+                                                const node *right_sibling,
+                                                bool is_root) const
+{
+  this->base_invariant_checker(min_key, max_key, is_root);
+  size_t n = this->key_slots_used();
+  for (size_t i = 0; i <= n; i++) {
+    ALWAYS_ASSERT(this->children_[i] != NULL);
+    if (i == 0) {
+      const node *left_child_sibling = NULL;
+      if (left_sibling)
+        left_child_sibling = AsInternal(left_sibling)->children_[left_sibling->key_slots_used()];
+      this->children_[0]->invariant_checker(min_key, &this->keys_[0], left_child_sibling, this->children_[i + 1], false);
+    } else if (i == n) {
+      const node *right_child_sibling = NULL;
+      if (right_sibling)
+        right_child_sibling = AsInternal(right_sibling)->children_[0];
+      this->children_[n]->invariant_checker(&this->keys_[n - 1], max_key, this->children_[i - 1], right_child_sibling, false);
+    } else {
+      this->children_[i]->invariant_checker(&this->keys_[i - 1], &this->keys_[i], this->children_[i - 1], this->children_[i + 1], false);
+    }
+  }
+  if (!n || this->children_[0]->is_internal_node())
+    return;
+  for (size_t i = 0; i <= n; i++) {
+    const node *left_child_sibling = NULL;
+    const node *right_child_sibling = NULL;
+    if (left_sibling)
+      left_child_sibling = AsInternal(left_sibling)->children_[left_sibling->key_slots_used()];
+    if (right_sibling)
+      right_child_sibling = AsInternal(right_sibling)->children_[0];
+    const leaf_node *child_prev = (i == 0) ? AsLeaf(left_child_sibling) : AsLeaf(this->children_[i - 1]);
+    const leaf_node *child_next = (i == n) ? AsLeaf(right_child_sibling) : AsLeaf(this->children_[i + 1]);
+    ALWAYS_ASSERT(AsLeaf(this->children_[i])->prev_ == child_prev);
+    ALWAYS_ASSERT(AsLeaf(this->children_[i])->next_ == child_next);
+  }
+}
+
+template <typename P>
+void
+btree<P>::recursive_delete(node *n)
+{
+  if (leaf_node *leaf = AsLeafCheck(n)) {
+#ifdef CHECK_INVARIANTS
+    leaf->lock();
+    leaf->mark_deleting();
+    leaf->unlock();
+#endif
+    size_t n = leaf->key_slots_used();
+    for (size_t i = 0; i < n; i++)
+      if (leaf->is_layer(i))
+        recursive_delete(leaf->values_[i].n_);
+    leaf_node::deleter(leaf);
+  } else {
+    internal_node *internal = AsInternal(n);
+    size_t n = internal->key_slots_used();
+    for (size_t i = 0; i < n + 1; i++)
+      recursive_delete(internal->children_[i]);
+#ifdef CHECK_INVARIANTS
+    internal->lock();
+    internal->mark_deleting();
+    internal->unlock();
+#endif
+    internal_node::deleter(internal);
+  }
+}
+
+//STATIC_COUNTER_DECL(scopedperf::tsc_ctr, btree_search_impl_tsc, btree_search_impl_perf_cg);
+
+template <typename P>
+bool
+btree<P>::search_impl(const key_type &k, value_type &v,
+                      typename util::vec<leaf_node *>::type &leaf_nodes,
+                      versioned_node_t *search_info) const
+{
+  INVARIANT(rcu::s_instance.in_rcu_region());
+  //ANON_REGION("btree<P>::search_impl:", &btree_search_impl_perf_cg);
+  INVARIANT(leaf_nodes.empty());
+
+retry:
+  node *cur;
+  key_type kcur;
+  uint64_t kslice;
+  size_t kslicelen;
+  if (likely(leaf_nodes.empty())) {
+    kcur = k;
+    cur = root_;
+    kslice = k.slice();
+    kslicelen = std::min(k.size(), size_t(9));
+  } else {
+    kcur = k.shift_many(leaf_nodes.size() - 1);
+    cur = leaf_nodes.back();
+    kslice = kcur.slice();
+    kslicelen = std::min(kcur.size(), size_t(9));
+    leaf_nodes.pop_back();
+  }
+
+  while (true) {
+    // each iteration of this while loop tries to descend
+    // down node "cur", looking for key kcur
+
+process:
+    uint64_t version = cur->stable_version();
+    if (unlikely(RawVersionManip::IsDeleting(version)))
+      // XXX: maybe we can only retry at the parent of this node, not the
+      // root node of the b-tree *layer*
+      goto retry;
+    if (leaf_node *leaf = AsLeafCheck(cur, version)) {
+      leaf->prefetch();
+      if (search_info) {
+        search_info->first = leaf;
+        search_info->second = RawVersionManip::Version(version);
+      }
+      key_search_ret kret = leaf->key_search(kslice, kslicelen);
+      ssize_t ret = kret.first;
+      if (ret != -1) {
+        // found
+        typename leaf_node::value_or_node_ptr vn = leaf->values_[ret];
+        const bool is_layer = leaf->is_layer(ret);
+        INVARIANT(!is_layer || kslicelen == 9);
+        varkey suffix(leaf->suffix(ret));
+        if (unlikely(!leaf->check_version(version)))
+          goto process;
+        leaf_nodes.push_back(leaf);
+
+        if (!is_layer) {
+          // check suffixes
+          if (kslicelen == 9 && suffix != kcur.shift())
+            return false;
+          v = vn.v_;
+          return true;
+        }
+
+        // search the next layer
+        cur = vn.n_;
+        kcur = kcur.shift();
+        kslice = kcur.slice();
+        kslicelen = std::min(kcur.size(), size_t(9));
+        continue;
+      }
+
+      // leaf might have lost responsibility for key k during the descend. we
+      // need to check this and adjust accordingly
+      if (unlikely(kslice < leaf->min_key_)) {
+        // try to go left
+        leaf_node *left_sibling = leaf->prev_;
+        if (unlikely(!leaf->check_version(version)))
+          goto process;
+        if (likely(left_sibling)) {
+          cur = left_sibling;
+          continue;
+        } else {
+          // XXX: this case shouldn't be possible...
+          goto retry;
+        }
+      } else {
+        // try to go right
+        leaf_node *right_sibling = leaf->next_;
+        if (unlikely(!leaf->check_version(version)))
+          goto process;
+        if (unlikely(!right_sibling)) {
+          leaf_nodes.push_back(leaf);
+          return false;
+        }
+        right_sibling->prefetch();
+        uint64_t right_version = right_sibling->stable_version();
+        key_slice right_min_key = right_sibling->min_key_;
+        if (unlikely(!right_sibling->check_version(right_version)))
+          goto process;
+        if (unlikely(kslice >= right_min_key)) {
+          cur = right_sibling;
+          continue;
+        }
+      }
+
+      leaf_nodes.push_back(leaf);
+      return false;
+    } else {
+      internal_node *internal = AsInternal(cur);
+      internal->prefetch();
+      key_search_ret kret = internal->key_lower_bound_search(kslice);
+      ssize_t ret = kret.first;
+      if (ret != -1)
+        cur = internal->children_[ret + 1];
+      else
+        cur = internal->children_[0];
+      if (unlikely(!internal->check_version(version)))
+        goto process;
+      INVARIANT(kret.second);
+    }
+  }
+}
+
+template <typename S>
+class string_restore {
+public:
+  inline string_restore(S &s, size_t n)
+    : s_(&s), n_(n) {}
+  inline ~string_restore()
+  {
+    s_->resize(n_);
+  }
+private:
+  S *s_;
+  size_t n_;
+};
+
+// recursively read the range from the layer down
+//
+// all args relative to prefix.
+//
+// prefix is non-const, meaning that this function might modify the contents.
+// it is guaranteed, however, to restore prefix to the state it was before the
+// invocation
+//
+// returns true if we keep going
+template <typename P>
+bool
+btree<P>::search_range_at_layer(
+    leaf_node *leaf,
+    string_type &prefix,
+    const key_type &lower,
+    bool inc_lower,
+    const key_type *upper,
+    low_level_search_range_callback &callback) const
+{
+  VERBOSE(std::cerr << "search_range_at_layer: prefix.size()=" << prefix.size() << std::endl);
+
+  key_slice last_keyslice = 0;
+  size_t last_keyslice_len = 0;
+  bool emitted_last_keyslice = false;
+  if (!inc_lower) {
+    last_keyslice = lower.slice();
+    last_keyslice_len = std::min(lower.size(), size_t(9));
+    emitted_last_keyslice = true;
+  }
+
+  key_slice lower_slice = lower.slice();
+  key_slice next_key = lower_slice;
+  const size_t prefix_size = prefix.size();
+  // NB: DON'T CALL RESERVE()
+  //prefix.reserve(prefix_size + 8); // allow for next layer
+  const uint64_t upper_slice = upper ? upper->slice() : 0;
+  string_restore<string_type> restorer(prefix, prefix_size);
+  while (!upper || next_key <= upper_slice) {
+    leaf->prefetch();
+
+    typename util::vec<leaf_kvinfo>::type buf;
+    const uint64_t version = leaf->stable_version();
+    key_slice leaf_min_key = leaf->min_key_;
+    if (leaf_min_key > next_key) {
+      // go left
+      leaf_node *left_sibling = leaf->prev_;
+      if (unlikely(!leaf->check_version(version)))
+        // try this node again
+        continue;
+      // try from left_sibling
+      leaf = left_sibling;
+      INVARIANT(leaf);
+      continue;
+    }
+
+    // grab all keys in [lower_slice, upper_slice]. we'll do boundary condition
+    // checking later (outside of the critical section)
+    for (size_t i = 0; i < leaf->key_slots_used(); i++) {
+      // XXX(stephentu): this 1st filter is overly conservative and we can do
+      // better
+      if ((leaf->keys_[i] > lower_slice ||
+           (leaf->keys_[i] == lower_slice &&
+            leaf->keyslice_length(i) >= std::min(lower.size(), size_t(9)))) &&
+          (!upper || leaf->keys_[i] <= upper_slice))
+        buf.emplace_back(
+            leaf->keys_[i],
+            leaf->values_[i],
+            leaf->is_layer(i),
+            leaf->keyslice_length(i),
+            leaf->suffix(i));
+    }
+
+    leaf_node *const right_sibling = leaf->next_;
+    key_slice leaf_max_key = right_sibling ? right_sibling->min_key_ : 0;
+
+    if (unlikely(!leaf->check_version(version)))
+      continue;
+
+    callback.on_resp_node(leaf, RawVersionManip::Version(version));
+
+    for (size_t i = 0; i < buf.size(); i++) {
+      // check to see if we already omitted a key <= buf[i]: if so, don't omit it
+      if (emitted_last_keyslice &&
+          ((buf[i].key_ < last_keyslice) ||
+           (buf[i].key_ == last_keyslice && buf[i].length_ <= last_keyslice_len)))
+        continue;
+      const size_t ncpy = std::min(buf[i].length_, size_t(8));
+      // XXX: prefix.end() calls _M_leak()
+      //prefix.replace(prefix.begin() + prefix_size, prefix.end(), buf[i].keyslice(), ncpy);
+      prefix.replace(prefix_size, string_type::npos, buf[i].keyslice(), ncpy);
+      if (buf[i].layer_) {
+        // recurse into layer
+        leaf_node *const next_layer = leftmost_descend_layer(buf[i].vn_.n_);
+        varkey zerokey;
+        if (emitted_last_keyslice && last_keyslice == buf[i].key_)
+          // NB(stephentu): this is implied by the filter above
+          INVARIANT(last_keyslice_len <= 8);
+        if (!search_range_at_layer(next_layer, prefix, zerokey, false, NULL, callback))
+          return false;
+      } else {
+        // check if we are before the start
+        if (buf[i].key_ == lower_slice) {
+          if (buf[i].length_ <= 8) {
+            if (buf[i].length_ < lower.size())
+              // skip
+              continue;
+          } else {
+            INVARIANT(buf[i].length_ == 9);
+            if (lower.size() > 8 && buf[i].suffix_ < lower.shift())
+              // skip
+              continue;
+          }
+        }
+
+        // check if we are after the end
+        if (upper && buf[i].key_ == upper_slice) {
+          if (buf[i].length_ == 9) {
+            if (upper->size() <= 8)
+              break;
+            if (buf[i].suffix_ >= upper->shift())
+              break;
+          } else if (buf[i].length_ >= upper->size()) {
+            break;
+          }
+        }
+        if (buf[i].length_ == 9)
+          prefix.append((const char *) buf[i].suffix_.data(), buf[i].suffix_.size());
+        // we give the actual version # minus all the other bits, b/c they are not
+        // important here and make comparison easier at higher layers
+        if (!callback.invoke(prefix, buf[i].vn_.v_, leaf, RawVersionManip::Version(version)))
+          return false;
+      }
+      last_keyslice = buf[i].key_;
+      last_keyslice_len = buf[i].length_;
+      emitted_last_keyslice = true;
+    }
+
+    if (!right_sibling)
+      // we're done
+      return true;
+
+    next_key = leaf_max_key;
+    leaf = right_sibling;
+  }
+
+  return true;
+}
+
+template <typename P>
+void
+btree<P>::search_range_call(const key_type &lower,
+                            const key_type *upper,
+                            low_level_search_range_callback &callback,
+                            string_type *buf) const
+{
+  rcu_region guard;
+  INVARIANT(rcu::s_instance.in_rcu_region());
+  if (unlikely(upper && *upper <= lower))
+    return;
+  typename util::vec<leaf_node *>::type leaf_nodes;
+  value_type v = 0;
+  search_impl(lower, v, leaf_nodes);
+  INVARIANT(!leaf_nodes.empty());
+  bool first = true;
+  string_type prefix_tmp, *prefix_px;
+  if (buf)
+    prefix_px = buf;
+  else
+    prefix_px = &prefix_tmp;
+  string_type &prefix(*prefix_px);
+  INVARIANT(prefix.empty());
+  prefix.assign((const char *) lower.data(), 8 * (leaf_nodes.size() - 1));
+  while (!leaf_nodes.empty()) {
+    leaf_node *cur = leaf_nodes.back();
+    leaf_nodes.pop_back();
+    key_type layer_upper;
+    bool layer_has_upper = false;
+    if (upper && upper->size() >= (8 * leaf_nodes.size())) {
+      layer_upper = upper->shift_many(leaf_nodes.size());
+      layer_has_upper = true;
+    }
+#ifdef CHECK_INVARIANTS
+    string_type prefix_before(prefix);
+#endif
+    if (!search_range_at_layer(
+          cur, prefix, lower.shift_many(leaf_nodes.size()),
+          first, layer_has_upper ? &layer_upper : NULL, callback))
+      return;
+#ifdef CHECK_INVARIANTS
+    INVARIANT(prefix == prefix_before);
+#endif
+    first = false;
+    if (!leaf_nodes.empty()) {
+      INVARIANT(prefix.size() >= 8);
+      prefix.resize(prefix.size() - 8);
+    }
+  }
+}
+
+template <typename P>
+bool
+btree<P>::remove_stable_location(node **root_location, const key_type &k, value_type *old_v)
+{
+  INVARIANT(rcu::s_instance.in_rcu_region());
+retry:
+  key_slice new_key;
+  node *replace_node = NULL;
+  typename util::vec<remove_parent_entry>::type parents;
+  typename util::vec<node *>::type locked_nodes;
+  node *local_root = *root_location;
+  remove_status status = remove0(local_root,
+      NULL, /* min_key */
+      NULL, /* max_key */
+      k,
+      old_v,
+      NULL, /* left_node */
+      NULL, /* right_node */
+      new_key,
+      replace_node,
+      parents,
+      locked_nodes);
+  switch (status) {
+  case R_NONE_NOMOD:
+    return false;
+  case R_NONE_MOD:
+    return true;
+  case R_RETRY:
+    goto retry;
+  case R_REPLACE_NODE:
+    INVARIANT(local_root->is_deleting());
+    INVARIANT(local_root->is_lock_owner());
+    INVARIANT(local_root->is_root());
+    INVARIANT(local_root == *root_location);
+    replace_node->set_root();
+    local_root->clear_root();
+    COMPILER_MEMORY_FENCE;
+    *root_location = replace_node;
+    // locks are still held here
+    return UnlockAndReturn(locked_nodes, true);
+  default:
+    ALWAYS_ASSERT(false);
+    return false;
+  }
+  ALWAYS_ASSERT(false);
+  return false;
+}
+
+template <typename P>
+typename btree<P>::leaf_node *
+btree<P>::leftmost_descend_layer(node *n) const
+{
+  node *cur = n;
+  while (true) {
+    if (leaf_node *leaf = AsLeafCheck(cur))
+      return leaf;
+    internal_node *internal = AsInternal(cur);
+    uint64_t version = cur->stable_version();
+    node *child = internal->children_[0];
+    if (unlikely(!internal->check_version(version)))
+      continue;
+    cur = child;
+  }
+}
+
+template <typename P>
+void
+btree<P>::tree_walk(tree_walk_callback &callback) const
+{
+  rcu_region guard;
+  INVARIANT(rcu::s_instance.in_rcu_region());
+  std::vector<node *> q;
+  // XXX: not sure if cast is safe
+  q.push_back((node *) root_);
+  while (!q.empty()) {
+    node *cur = q.back();
+    q.pop_back();
+    cur->prefetch();
+    leaf_node *leaf = leftmost_descend_layer(cur);
+    INVARIANT(leaf);
+    while (leaf) {
+      leaf->prefetch();
+    process:
+      const uint64_t version = leaf->stable_version();
+      const size_t n = leaf->key_slots_used();
+      std::vector<node *> layers;
+      for (size_t i = 0; i < n; i++)
+        if (leaf->is_layer(i))
+          layers.push_back(leaf->values_[i].n_);
+      leaf_node *next = leaf->next_;
+      callback.on_node_begin(leaf);
+      if (unlikely(!leaf->check_version(version))) {
+        callback.on_node_failure();
+        goto process;
+      }
+      callback.on_node_success();
+      leaf = next;
+      q.insert(q.end(), layers.begin(), layers.end());
+    }
+  }
+}
+
+template <typename P>
+void
+btree<P>::size_walk_callback::on_node_begin(const node_opaque_t *n)
+{
+  INVARIANT(n->is_leaf_node());
+  INVARIANT(spec_size_ == 0);
+  const leaf_node *leaf = (const leaf_node *) n;
+  const size_t sz = leaf->key_slots_used();
+  for (size_t i = 0; i < sz; i++)
+    if (!leaf->is_layer(i))
+      spec_size_++;
+}
+
+template <typename P>
+void
+btree<P>::size_walk_callback::on_node_success()
+{
+  size_ += spec_size_;
+  spec_size_ = 0;
+}
+
+template <typename P>
+void
+btree<P>::size_walk_callback::on_node_failure()
+{
+  spec_size_ = 0;
+}
+
+template <typename P>
+typename btree<P>::leaf_node *
+btree<P>::FindRespLeafNode(
+      leaf_node *leaf,
+      uint64_t kslice,
+      uint64_t &version)
+{
+retry:
+  version = leaf->stable_version();
+  if (unlikely(leaf->is_deleting())) {
+    leaf_node *left = leaf->prev_;
+    if (left) {
+      leaf = left;
+      goto retry;
+    }
+    leaf_node *right = leaf->next_;
+    if (right) {
+      leaf = right;
+      goto retry;
+    }
+    // XXX(stephentu): not sure if we can *really* depend on this,
+    // need to convince ourselves why this is not possible!
+    ALWAYS_ASSERT(false);
+  }
+  if (unlikely(kslice < leaf->min_key_)) {
+    // we need to go left
+    leaf_node *left = leaf->prev_;
+    if (left)
+      leaf = left;
+    goto retry;
+  }
+  leaf_node *right = leaf->next_;
+
+  // NB(stephentu): the only way for right->min_key_ to decrease, is for the
+  // current leaf node to split. therefore, it is un-necessary to ensure stable
+  // version on the next node (we only need to ensure it on the current node)
+  if (likely(right) && unlikely(kslice >= right->min_key_)) {
+    leaf = right;
+    goto retry;
+  }
+
+  //if (likely(right)) {
+  //  const uint64_t right_version = right->stable_version();
+  //  const uint64_t right_min_key = right->min_key_;
+  //  if (unlikely(!right->check_version(right_version)))
+  //    goto retry;
+  //  if (unlikely(kslice >= right_min_key)) {
+  //    leaf = right;
+  //    goto retry;
+  //  }
+  //}
+
+  return leaf;
+}
+
+template <typename P>
+typename btree<P>::leaf_node *
+btree<P>::FindRespLeafLowerBound(
+      leaf_node *leaf,
+      uint64_t kslice,
+      size_t kslicelen,
+      uint64_t &version,
+      size_t &n,
+      ssize_t &idxmatch,
+      ssize_t &idxlowerbound)
+{
+  leaf = FindRespLeafNode(leaf, kslice, version);
+
+  // use 0 for slice length, so we can a pointer <= all elements
+  // with the same slice
+  const key_search_ret kret = leaf->key_lower_bound_search(kslice, 0);
+  const ssize_t ret = kret.first;
+  n = kret.second;
+
+  // count the number of values already here with the same kslice.
+  idxmatch = -1;
+  idxlowerbound = ret;
+  for (size_t i = (ret == -1 ? 0 : ret); i < n; i++) {
+    if (leaf->keys_[i] < kslice) {
+      continue;
+    } else if (leaf->keys_[i] == kslice) {
+      const size_t kslicelen0 = leaf->keyslice_length(i);
+      if (kslicelen0 <= kslicelen) {
+        // invariant doesn't hold, b/c values can be changing
+        // concurrently (leaf is not assumed to be locked)
+        //INVARIANT(idxmatch == -1);
+        idxlowerbound = i;
+        if (kslicelen0 == kslicelen)
+          idxmatch = i;
+      }
+    } else {
+      break;
+    }
+  }
+
+  return leaf;
+}
+
+template <typename P>
+typename btree<P>::leaf_node *
+btree<P>::FindRespLeafExact(
+      leaf_node *leaf,
+      uint64_t kslice,
+      size_t kslicelen,
+      uint64_t &version,
+      size_t &n,
+      ssize_t &idxmatch)
+{
+  leaf = FindRespLeafNode(leaf, kslice, version);
+  key_search_ret kret = leaf->key_search(kslice, kslicelen);
+  idxmatch = kret.first;
+  n = kret.second;
+  return leaf;
+}
+
+template <typename P>
+typename btree<P>::insert_status
+btree<P>::insert0(node *np,
+                  const key_type &k,
+                  value_type v,
+                  bool only_if_absent,
+                  value_type *old_v,
+                  insert_info_t *insert_info,
+                  key_slice &min_key,
+                  node *&new_node,
+                  typename util::vec<insert_parent_entry>::type &parents,
+                  typename util::vec<node *>::type &locked_nodes)
+{
+  uint64_t kslice = k.slice();
+  size_t kslicelen = std::min(k.size(), size_t(9));
+
+  np->prefetch();
+  if (leaf_node *leaf = AsLeafCheck(np)) {
+    // locked nodes are acquired bottom to top
+    INVARIANT(locked_nodes.empty());
+
+retry_cur_leaf:
+    uint64_t version;
+    size_t n;
+    ssize_t lenmatch, lenlowerbound;
+    leaf_node *resp_leaf = FindRespLeafLowerBound(
+        leaf, kslice, kslicelen, version, n, lenmatch, lenlowerbound);
+
+    // len match case
+    if (lenmatch != -1) {
+      // exact match case
+      if (kslicelen <= 8 ||
+          (!resp_leaf->is_layer(lenmatch) &&
+           resp_leaf->suffix(lenmatch) == k.shift())) {
+        const uint64_t locked_version = resp_leaf->lock();
+        if (unlikely(!btree::CheckVersion(version, locked_version))) {
+          resp_leaf->unlock();
+          goto retry_cur_leaf;
+        }
+        locked_nodes.push_back(resp_leaf);
+        // easy case- we don't modify the node itself
+        if (old_v)
+          *old_v = resp_leaf->values_[lenmatch].v_;
+        if (!only_if_absent)
+          resp_leaf->values_[lenmatch].v_ = v;
+        if (insert_info)
+          insert_info->node = 0;
+        return UnlockAndReturn(locked_nodes, I_NONE_NOMOD);
+      }
+      INVARIANT(kslicelen == 9);
+      if (resp_leaf->is_layer(lenmatch)) {
+        node *subroot = resp_leaf->values_[lenmatch].n_;
+        INVARIANT(subroot);
+        if (unlikely(!resp_leaf->check_version(version)))
+          goto retry_cur_leaf;
+        key_slice mk;
+        node *ret;
+        typename util::vec<insert_parent_entry>::type subparents;
+        typename util::vec<node *>::type sub_locked_nodes;
+        const insert_status status =
+          insert0(subroot, k.shift(), v, only_if_absent, old_v, insert_info,
+              mk, ret, subparents, sub_locked_nodes);
+
+        switch (status) {
+        case I_NONE_NOMOD:
+        case I_NONE_MOD:
+        case I_RETRY:
+          INVARIANT(sub_locked_nodes.empty());
+          return status;
+
+        case I_SPLIT:
+          // the subroot split, so we need to find the leaf again, lock the
+          // node, and create a new internal node
+
+          INVARIANT(ret);
+          INVARIANT(ret->key_slots_used() > 0);
+
+          for (;;) {
+            resp_leaf = FindRespLeafLowerBound(
+                resp_leaf, kslice, kslicelen, version, n, lenmatch, lenlowerbound);
+            const uint64_t locked_version = resp_leaf->lock();
+            if (likely(btree::CheckVersion(version, locked_version))) {
+              locked_nodes.push_back(resp_leaf);
+              break;
+            }
+            resp_leaf->unlock();
+          }
+
+          INVARIANT(lenmatch != -1);
+          INVARIANT(resp_leaf->is_layer(lenmatch));
+          subroot = resp_leaf->values_[lenmatch].n_;
+          INVARIANT(subroot->is_modifying());
+          INVARIANT(subroot->is_lock_owner());
+          INVARIANT(subroot->is_root());
+
+          internal_node *new_root = internal_node::alloc();
+#ifdef CHECK_INVARIANTS
+          new_root->lock();
+          new_root->mark_modifying();
+          locked_nodes.push_back(new_root);
+#endif /* CHECK_INVARIANTS */
+          new_root->children_[0] = subroot;
+          new_root->children_[1] = ret;
+          new_root->keys_[0] = mk;
+          new_root->set_key_slots_used(1);
+          new_root->set_root();
+          subroot->clear_root();
+          resp_leaf->values_[lenmatch].n_ = new_root;
+
+          // locks are still held here
+          UnlockNodes(sub_locked_nodes);
+          return UnlockAndReturn(locked_nodes, I_NONE_MOD);
+        }
+        ALWAYS_ASSERT(false);
+
+      } else {
+        const uint64_t locked_version = resp_leaf->lock();
+        if (unlikely(!btree::CheckVersion(version, locked_version))) {
+          resp_leaf->unlock();
+          goto retry_cur_leaf;
+        }
+        locked_nodes.push_back(resp_leaf);
+
+        INVARIANT(resp_leaf->suffixes_); // b/c lenmatch != -1 and this is not a layer
+        // need to create a new btree layer, and add both existing key and
+        // new key to it
+
+        // XXX: need to mark modifying because we cannot change both the
+        // value and the type atomically
+        resp_leaf->mark_modifying();
+
+        leaf_node *new_root = leaf_node::alloc();
+#ifdef CHECK_INVARIANTS
+        new_root->lock();
+        new_root->mark_modifying();
+#endif /* CHECK_INVARIANTS */
+        new_root->set_root();
+        varkey old_slice(resp_leaf->suffix(lenmatch));
+        new_root->keys_[0] = old_slice.slice();
+        new_root->values_[0] = resp_leaf->values_[lenmatch];
+        new_root->keyslice_set_length(0, std::min(old_slice.size(), size_t(9)), false);
+        new_root->inc_key_slots_used();
+        if (new_root->keyslice_length(0) == 9) {
+          new_root->alloc_suffixes();
+          rcu_imstring i(old_slice.data() + 8, old_slice.size() - 8);
+          new_root->suffixes_[0].swap(i);
+        }
+        resp_leaf->values_[lenmatch].n_ = new_root;
+        {
+          rcu_imstring i;
+          resp_leaf->suffixes_[lenmatch].swap(i);
+        }
+        resp_leaf->value_set_layer(lenmatch);
+#ifdef CHECK_INVARIANTS
+        new_root->unlock();
+#endif /* CHECK_INVARIANTS */
+
+        key_slice mk;
+        node *ret;
+        typename util::vec<insert_parent_entry>::type subparents;
+        typename util::vec<node *>::type sub_locked_nodes;
+        const insert_status status =
+          insert0(new_root, k.shift(), v, only_if_absent, old_v, insert_info,
+              mk, ret, subparents, sub_locked_nodes);
+        if (status != I_NONE_MOD)
+          INVARIANT(false);
+        INVARIANT(sub_locked_nodes.empty());
+        return UnlockAndReturn(locked_nodes, I_NONE_MOD);
+      }
+    }
+
+    // lenlowerbound + 1 is the slot (0-based index) we want the new key to go
+    // into, in the leaf node
+    if (n < NKeysPerNode) {
+      const uint64_t locked_version = resp_leaf->lock();
+      if (unlikely(!btree::CheckVersion(version, locked_version))) {
+        resp_leaf->unlock();
+        goto retry_cur_leaf;
+      }
+      locked_nodes.push_back(resp_leaf);
+
+      // also easy case- we only need to make local modifications
+      resp_leaf->mark_modifying();
+
+      sift_right(resp_leaf->keys_, lenlowerbound + 1, n);
+      resp_leaf->keys_[lenlowerbound + 1] = kslice;
+      sift_right(resp_leaf->values_, lenlowerbound + 1, n);
+      resp_leaf->values_[lenlowerbound + 1].v_ = v;
+      sift_right(resp_leaf->lengths_, lenlowerbound + 1, n);
+      resp_leaf->keyslice_set_length(lenlowerbound + 1, kslicelen, false);
+      if (resp_leaf->suffixes_)
+        sift_swap_right(resp_leaf->suffixes_, lenlowerbound + 1, n);
+      if (kslicelen == 9) {
+        resp_leaf->ensure_suffixes();
+        rcu_imstring i(k.data() + 8, k.size() - 8);
+        resp_leaf->suffixes_[lenlowerbound + 1].swap(i);
+      } else if (resp_leaf->suffixes_) {
+        rcu_imstring i;
+        resp_leaf->suffixes_[lenlowerbound + 1].swap(i);
+      }
+      resp_leaf->inc_key_slots_used();
+
+//#ifdef CHECK_INVARIANTS
+//      resp_leaf->base_invariant_unique_keys_check();
+//#endif
+      if (insert_info) {
+        insert_info->node = resp_leaf;
+        insert_info->old_version = RawVersionManip::Version(resp_leaf->unstable_version()); // we hold lock on leaf
+        insert_info->new_version = insert_info->old_version + 1;
+      }
+      return UnlockAndReturn(locked_nodes, I_NONE_MOD);
+    } else {
+      INVARIANT(n == NKeysPerNode);
+
+      if (unlikely(resp_leaf != leaf))
+        // sigh, we really do need parent points- if resp_leaf != leaf, then
+        // all the parent points we saved on the way down are no longer valid
+        return UnlockAndReturn(locked_nodes, I_RETRY);
+      const uint64_t locked_version = resp_leaf->lock();
+      if (unlikely(!btree::CheckVersion(version, locked_version))) {
+        resp_leaf->unlock();
+        goto retry_cur_leaf;
+      }
+      locked_nodes.push_back(resp_leaf);
+
+      // we need to split the current node, potentially causing a bunch of
+      // splits to happen in ancestors. to make this safe w/o
+      // using a very complicated locking protocol, we will first acquire all
+      // locks on nodes which will be modified, in left-to-right,
+      // bottom-to-top order
+
+      if (parents.empty()) {
+        if (unlikely(!resp_leaf->is_root()))
+          return UnlockAndReturn(locked_nodes, I_RETRY);
+        //INVARIANT(resp_leaf == root);
+      } else {
+        for (auto rit = parents.rbegin(); rit != parents.rend(); ++rit) {
+          // lock the parent
+          node *p = rit->first;
+          p->lock();
+          locked_nodes.push_back(p);
+          if (unlikely(!p->check_version(rit->second)))
+            // in traversing down the tree, an ancestor of this node was
+            // modified- to be safe, we start over
+            return UnlockAndReturn(locked_nodes, I_RETRY);
+          if ((rit + 1) == parents.rend()) {
+            // did the root change?
+            if (unlikely(!p->is_root()))
+              return UnlockAndReturn(locked_nodes, I_RETRY);
+            //INVARIANT(p == root);
+          }
+
+          // since the child needs a split, see if we have room in the parent-
+          // if we don't have room, we'll also need to split the parent, in which
+          // case we must grab its parent's lock
+          INVARIANT(p->is_internal_node());
+          size_t parent_n = p->key_slots_used();
+          INVARIANT(parent_n > 0 && parent_n <= NKeysPerNode);
+          if (parent_n < NKeysPerNode)
+            // can stop locking up now, since this node won't split
+            break;
+        }
+      }
+
+      // at this point, we have locked all nodes which will be split/modified
+      // modulo the new nodes to be created
+      resp_leaf->mark_modifying();
+
+      leaf_node *new_leaf = leaf_node::alloc();
+      new_leaf->prefetch();
+
+#ifdef CHECK_INVARIANTS
+      new_leaf->lock();
+      new_leaf->mark_modifying();
+      locked_nodes.push_back(new_leaf);
+#endif /* CHECK_INVARIANTS */
+
+      if (!resp_leaf->next_ && resp_leaf->keys_[n - 1] < kslice) {
+        // sequential insert optimization- in this case, we don't bother
+        // splitting the node. instead, keep the current leaf node full, and
+        // insert the new key into the new leaf node (violating the btree invariant)
+        //
+        // this optimization is commonly implemented, including in masstree and
+        // berkeley db- w/o this optimization, sequential inserts leave the all
+        // nodes half full
+
+        new_leaf->keys_[0] = kslice;
+        new_leaf->values_[0].v_ = v;
+        new_leaf->keyslice_set_length(0, kslicelen, false);
+        if (kslicelen == 9) {
+          new_leaf->alloc_suffixes();
+          rcu_imstring i(k.data() + 8, k.size() - 8);
+          new_leaf->suffixes_[0].swap(i);
+        }
+        new_leaf->set_key_slots_used(1);
+
+      } else {
+        // regular case
+
+        // compute how many keys the smaller node would have if we put the
+        // new key in the left (old) or right (new) side of the split.
+        // then choose the split which maximizes the mininum.
+        //
+        // XXX: do this in a more elegant way
+
+        // a split point S is a number such that indices [0, s) go into the left
+        // partition, and indices [s, N) go into the right partition
+        size_t left_split_point, right_split_point;
+
+        left_split_point = NKeysPerNode / 2;
+        for (ssize_t i = left_split_point - 1; i >= 0; i--) {
+          if (likely(resp_leaf->keys_[i] != resp_leaf->keys_[left_split_point]))
+            break;
+          left_split_point--;
+        }
+        INVARIANT(left_split_point <= NKeysPerNode);
+        INVARIANT(left_split_point == 0 || resp_leaf->keys_[left_split_point - 1] != resp_leaf->keys_[left_split_point]);
+
+        right_split_point = NKeysPerNode / 2;
+        for (ssize_t i = right_split_point - 1; i >= 0 && i < ssize_t(NKeysPerNode) - 1; i++) {
+          if (likely(resp_leaf->keys_[i] != resp_leaf->keys_[right_split_point]))
+            break;
+          right_split_point++;
+        }
+        INVARIANT(right_split_point <= NKeysPerNode);
+        INVARIANT(right_split_point == 0 || resp_leaf->keys_[right_split_point - 1] != resp_leaf->keys_[right_split_point]);
+
+        size_t split_point;
+        if (std::min(left_split_point, NKeysPerNode - left_split_point) <
+            std::min(right_split_point, NKeysPerNode - right_split_point))
+          split_point = right_split_point;
+        else
+          split_point = left_split_point;
+
+        if (split_point <= size_t(lenlowerbound + 1) && resp_leaf->keys_[split_point - 1] != kslice) {
+          // put new key in new leaf (right)
+          size_t pos = lenlowerbound + 1 - split_point;
+
+          copy_into(&new_leaf->keys_[0], resp_leaf->keys_, split_point, lenlowerbound + 1);
+          new_leaf->keys_[pos] = kslice;
+          copy_into(&new_leaf->keys_[pos + 1], resp_leaf->keys_, lenlowerbound + 1, NKeysPerNode);
+
+          copy_into(&new_leaf->values_[0], resp_leaf->values_, split_point, lenlowerbound + 1);
+          new_leaf->values_[pos].v_ = v;
+          copy_into(&new_leaf->values_[pos + 1], resp_leaf->values_, lenlowerbound + 1, NKeysPerNode);
+
+          copy_into(&new_leaf->lengths_[0], resp_leaf->lengths_, split_point, lenlowerbound + 1);
+          new_leaf->keyslice_set_length(pos, kslicelen, false);
+          copy_into(&new_leaf->lengths_[pos + 1], resp_leaf->lengths_, lenlowerbound + 1, NKeysPerNode);
+
+          if (resp_leaf->suffixes_) {
+            new_leaf->ensure_suffixes();
+            swap_with(&new_leaf->suffixes_[0], resp_leaf->suffixes_, split_point, lenlowerbound + 1);
+          }
+          if (kslicelen == 9) {
+            new_leaf->ensure_suffixes();
+            rcu_imstring i(k.data() + 8, k.size() - 8);
+            new_leaf->suffixes_[pos].swap(i);
+          } else if (new_leaf->suffixes_) {
+            rcu_imstring i;
+            new_leaf->suffixes_[pos].swap(i);
+          }
+          if (resp_leaf->suffixes_) {
+            new_leaf->ensure_suffixes();
+            swap_with(&new_leaf->suffixes_[pos + 1], resp_leaf->suffixes_, lenlowerbound + 1, NKeysPerNode);
+          }
+
+          resp_leaf->set_key_slots_used(split_point);
+          new_leaf->set_key_slots_used(NKeysPerNode - split_point + 1);
+
+#ifdef CHECK_INVARIANTS
+          resp_leaf->base_invariant_unique_keys_check();
+          new_leaf->base_invariant_unique_keys_check();
+          INVARIANT(resp_leaf->keys_[split_point - 1] < new_leaf->keys_[0]);
+#endif /* CHECK_INVARIANTS */
+
+        } else {
+          // XXX: not really sure if this invariant is true, but we rely
+          // on it for now
+          INVARIANT(size_t(lenlowerbound + 1) <= split_point);
+
+          // put new key in original leaf
+          copy_into(&new_leaf->keys_[0], resp_leaf->keys_, split_point, NKeysPerNode);
+          copy_into(&new_leaf->values_[0], resp_leaf->values_, split_point, NKeysPerNode);
+          copy_into(&new_leaf->lengths_[0], resp_leaf->lengths_, split_point, NKeysPerNode);
+          if (resp_leaf->suffixes_) {
+            new_leaf->ensure_suffixes();
+            swap_with(&new_leaf->suffixes_[0], resp_leaf->suffixes_, split_point, NKeysPerNode);
+          }
+
+          sift_right(resp_leaf->keys_, lenlowerbound + 1, split_point);
+          resp_leaf->keys_[lenlowerbound + 1] = kslice;
+          sift_right(resp_leaf->values_, lenlowerbound + 1, split_point);
+          resp_leaf->values_[lenlowerbound + 1].v_ = v;
+          sift_right(resp_leaf->lengths_, lenlowerbound + 1, split_point);
+          resp_leaf->keyslice_set_length(lenlowerbound + 1, kslicelen, false);
+          if (resp_leaf->suffixes_)
+            sift_swap_right(resp_leaf->suffixes_, lenlowerbound + 1, split_point);
+          if (kslicelen == 9) {
+            resp_leaf->ensure_suffixes();
+            rcu_imstring i(k.data() + 8, k.size() - 8);
+            resp_leaf->suffixes_[lenlowerbound + 1].swap(i);
+          } else if (resp_leaf->suffixes_) {
+            rcu_imstring i;
+            resp_leaf->suffixes_[lenlowerbound + 1].swap(i);
+          }
+
+          resp_leaf->set_key_slots_used(split_point + 1);
+          new_leaf->set_key_slots_used(NKeysPerNode - split_point);
+
+#ifdef CHECK_INVARIANTS
+          resp_leaf->base_invariant_unique_keys_check();
+          new_leaf->base_invariant_unique_keys_check();
+          INVARIANT(resp_leaf->keys_[split_point] < new_leaf->keys_[0]);
+#endif /* CHECK_INVARIANTS */
+        }
+      }
+
+      // pointer adjustment
+      new_leaf->prev_ = resp_leaf;
+      new_leaf->next_ = resp_leaf->next_;
+      if (resp_leaf->next_)
+        resp_leaf->next_->prev_ = new_leaf;
+      resp_leaf->next_ = new_leaf;
+
+      min_key = new_leaf->keys_[0];
+      new_leaf->min_key_ = min_key;
+      new_node = new_leaf;
+
+      if (insert_info) {
+        insert_info->node = resp_leaf;
+        insert_info->old_version = RawVersionManip::Version(resp_leaf->unstable_version()); // we hold lock on leaf
+        insert_info->new_version = insert_info->old_version + 1;
+      }
+
+      return I_SPLIT;
+    }
+  } else {
+    internal_node *internal = AsInternal(np);
+    uint64_t version = internal->stable_version();
+    if (unlikely(RawVersionManip::IsDeleting(version)))
+      return UnlockAndReturn(locked_nodes, I_RETRY);
+    key_search_ret kret = internal->key_lower_bound_search(kslice);
+    ssize_t ret = kret.first;
+    size_t n = kret.second;
+    size_t child_idx = (ret == -1) ? 0 : ret + 1;
+    node *child_ptr = internal->children_[child_idx];
+    if (unlikely(!internal->check_version(version)))
+      return UnlockAndReturn(locked_nodes, I_RETRY);
+    parents.push_back(insert_parent_entry(internal, version));
+    key_slice mk = 0;
+    node *new_child = NULL;
+    insert_status status =
+      insert0(child_ptr, k, v, only_if_absent, old_v, insert_info,
+              mk, new_child, parents, locked_nodes);
+    if (status != I_SPLIT) {
+      INVARIANT(locked_nodes.empty());
+      return status;
+    }
+    INVARIANT(new_child);
+    INVARIANT(internal->is_locked()); // previous call to insert0() must lock internal node for insertion
+    INVARIANT(internal->is_lock_owner());
+    INVARIANT(internal->check_version(version));
+    INVARIANT(new_child->key_slots_used() > 0);
+    INVARIANT(n > 0);
+    internal->mark_modifying();
+    if (n < NKeysPerNode) {
+      sift_right(internal->keys_, child_idx, n);
+      internal->keys_[child_idx] = mk;
+      sift_right(internal->children_, child_idx + 1, n + 1);
+      internal->children_[child_idx + 1] = new_child;
+      internal->inc_key_slots_used();
+      return UnlockAndReturn(locked_nodes, I_NONE_MOD);
+    } else {
+      INVARIANT(n == NKeysPerNode);
+      INVARIANT(ret == internal->key_lower_bound_search(mk).first);
+
+      internal_node *new_internal = internal_node::alloc();
+      new_internal->prefetch();
+#ifdef CHECK_INVARIANTS
+      new_internal->lock();
+      new_internal->mark_modifying();
+      locked_nodes.push_back(new_internal);
+#endif /* CHECK_INVARIANTS */
+
+      // there are three cases post-split:
+      // (1) mk goes in the original node
+      // (2) mk is the key we push up
+      // (3) mk goes in the new node
+
+      const ssize_t split_point = NMinKeysPerNode - 1;
+      if (ret < split_point) {
+        // case (1)
+        min_key = internal->keys_[split_point];
+
+        copy_into(&new_internal->keys_[0], internal->keys_, NMinKeysPerNode, NKeysPerNode);
+        copy_into(&new_internal->children_[0], internal->children_, NMinKeysPerNode, NKeysPerNode + 1);
+        new_internal->set_key_slots_used(NKeysPerNode - NMinKeysPerNode);
+
+        sift_right(internal->keys_, child_idx, NMinKeysPerNode - 1);
+        internal->keys_[child_idx] = mk;
+        sift_right(internal->children_, child_idx + 1, NMinKeysPerNode);
+        internal->children_[child_idx + 1] = new_child;
+        internal->set_key_slots_used(NMinKeysPerNode);
+
+      } else if (ret == split_point) {
+        // case (2)
+        min_key = mk;
+
+        copy_into(&new_internal->keys_[0], internal->keys_, NMinKeysPerNode, NKeysPerNode);
+        copy_into(&new_internal->children_[1], internal->children_, NMinKeysPerNode + 1, NKeysPerNode + 1);
+        new_internal->children_[0] = new_child;
+        new_internal->set_key_slots_used(NKeysPerNode - NMinKeysPerNode);
+        internal->set_key_slots_used(NMinKeysPerNode);
+
+      } else {
+        // case (3)
+        min_key = internal->keys_[NMinKeysPerNode];
+
+        size_t pos = child_idx - NMinKeysPerNode - 1;
+
+        copy_into(&new_internal->keys_[0], internal->keys_, NMinKeysPerNode + 1, child_idx);
+        new_internal->keys_[pos] = mk;
+        copy_into(&new_internal->keys_[pos + 1], internal->keys_, child_idx, NKeysPerNode);
+
+        copy_into(&new_internal->children_[0], internal->children_, NMinKeysPerNode + 1, child_idx + 1);
+        new_internal->children_[pos + 1] = new_child;
+        copy_into(&new_internal->children_[pos + 2], internal->children_, child_idx + 1, NKeysPerNode + 1);
+
+        new_internal->set_key_slots_used(NKeysPerNode - NMinKeysPerNode);
+        internal->set_key_slots_used(NMinKeysPerNode);
+      }
+
+      INVARIANT(internal->keys_[internal->key_slots_used() - 1] < new_internal->keys_[0]);
+      new_node = new_internal;
+      return I_SPLIT;
+    }
+  }
+}
+
+template <typename P>
+bool
+btree<P>::insert_stable_location(
+    node **root_location, const key_type &k, value_type v,
+    bool only_if_absent, value_type *old_v,
+    insert_info_t *insert_info)
+{
+  INVARIANT(rcu::s_instance.in_rcu_region());
+retry:
+  key_slice mk;
+  node *ret;
+  typename util::vec<insert_parent_entry>::type parents;
+  typename util::vec<node *>::type locked_nodes;
+  node *local_root = *root_location;
+  const insert_status status =
+    insert0(local_root, k, v, only_if_absent, old_v, insert_info,
+            mk, ret, parents, locked_nodes);
+  INVARIANT(status == I_SPLIT || locked_nodes.empty());
+  switch (status) {
+  case I_NONE_NOMOD:
+    return false;
+  case I_NONE_MOD:
+    return true;
+  case I_RETRY:
+    goto retry;
+  case I_SPLIT:
+    INVARIANT(ret);
+    INVARIANT(ret->key_slots_used() > 0);
+    INVARIANT(local_root->is_modifying());
+    INVARIANT(local_root->is_lock_owner());
+    INVARIANT(local_root->is_root());
+    INVARIANT(local_root == *root_location);
+    internal_node *new_root = internal_node::alloc();
+#ifdef CHECK_INVARIANTS
+    new_root->lock();
+    new_root->mark_modifying();
+    locked_nodes.push_back(new_root);
+#endif /* CHECK_INVARIANTS */
+    new_root->children_[0] = local_root;
+    new_root->children_[1] = ret;
+    new_root->keys_[0] = mk;
+    new_root->set_key_slots_used(1);
+    new_root->set_root();
+    local_root->clear_root();
+    COMPILER_MEMORY_FENCE;
+    *root_location = new_root;
+    // locks are still held here
+    return UnlockAndReturn(locked_nodes, true);
+  }
+  ALWAYS_ASSERT(false);
+  return false;
+}
+
+/**
+ * remove is very tricky to get right!
+ *
+ * XXX: optimize remove so it holds less locks
+ */
+template <typename P>
+typename btree<P>::remove_status
+btree<P>::remove0(node *np,
+                  key_slice *min_key,
+                  key_slice *max_key,
+                  const key_type &k,
+                  value_type *old_v,
+                  node *left_node,
+                  node *right_node,
+                  key_slice &new_key,
+                  node *&replace_node,
+                  typename util::vec<remove_parent_entry>::type &parents,
+                  typename util::vec<node *>::type &locked_nodes)
+{
+  uint64_t kslice = k.slice();
+  size_t kslicelen = std::min(k.size(), size_t(9));
+
+  np->prefetch();
+  if (leaf_node *leaf = AsLeafCheck(np)) {
+    INVARIANT(locked_nodes.empty());
+
+    SINGLE_THREADED_INVARIANT(!left_node || (leaf->prev_ == left_node && AsLeaf(left_node)->next == leaf));
+    SINGLE_THREADED_INVARIANT(!right_node || (leaf->next_ == right_node && AsLeaf(right_node)->prev == leaf));
+
+retry_cur_leaf:
+    uint64_t version;
+    size_t n;
+    ssize_t ret;
+    leaf_node *resp_leaf = FindRespLeafExact(
+        leaf, kslice, kslicelen, version, n, ret);
+
+    if (ret == -1) {
+      if (unlikely(!resp_leaf->check_version(version)))
+        goto retry_cur_leaf;
+      return UnlockAndReturn(locked_nodes, R_NONE_NOMOD);
+    }
+    if (kslicelen == 9) {
+      if (resp_leaf->is_layer(ret)) {
+        node *subroot = resp_leaf->values_[ret].n_;
+        INVARIANT(subroot);
+        if (unlikely(!resp_leaf->check_version(version)))
+          goto retry_cur_leaf;
+
+        key_slice new_key;
+        node *replace_node = NULL;
+        typename util::vec<remove_parent_entry>::type sub_parents;
+        typename util::vec<node *>::type sub_locked_nodes;
+        remove_status status = remove0(subroot,
+            NULL, /* min_key */
+            NULL, /* max_key */
+            k.shift(),
+            old_v,
+            NULL, /* left_node */
+            NULL, /* right_node */
+            new_key,
+            replace_node,
+            sub_parents,
+            sub_locked_nodes);
+        switch (status) {
+        case R_NONE_NOMOD:
+        case R_NONE_MOD:
+        case R_RETRY:
+          INVARIANT(sub_locked_nodes.empty());
+          return status;
+
+        case R_REPLACE_NODE:
+          INVARIANT(replace_node);
+          for (;;) {
+            resp_leaf = FindRespLeafExact(
+                resp_leaf, kslice, kslicelen, version, n, ret);
+            const uint64_t locked_version = resp_leaf->lock();
+            if (likely(btree::CheckVersion(version, locked_version))) {
+              locked_nodes.push_back(resp_leaf);
+              break;
+            }
+            resp_leaf->unlock();
+          }
+
+          INVARIANT(subroot->is_deleting());
+          INVARIANT(subroot->is_lock_owner());
+          INVARIANT(subroot->is_root());
+          replace_node->set_root();
+          subroot->clear_root();
+          resp_leaf->values_[ret].n_ = replace_node;
+
+          // XXX: need to re-merge back when layer size becomes 1, but skip this
+          // for now
+
+          // locks are still held here
+          UnlockNodes(sub_locked_nodes);
+          return UnlockAndReturn(locked_nodes, R_NONE_MOD);
+
+        default:
+          break;
+        }
+        ALWAYS_ASSERT(false);
+
+      } else {
+        // suffix check
+        if (resp_leaf->suffix(ret) != k.shift()) {
+          if (unlikely(!resp_leaf->check_version(version)))
+            goto retry_cur_leaf;
+          return UnlockAndReturn(locked_nodes, R_NONE_NOMOD);
+        }
+      }
+    }
+
+    //INVARIANT(!resp_leaf->is_layer(ret));
+    if (n > NMinKeysPerNode) {
+      const uint64_t locked_version = resp_leaf->lock();
+      if (unlikely(!btree::CheckVersion(version, locked_version))) {
+        resp_leaf->unlock();
+        goto retry_cur_leaf;
+      }
+      locked_nodes.push_back(resp_leaf);
+      if (old_v)
+        *old_v = resp_leaf->values_[ret].v_;
+      resp_leaf->mark_modifying();
+      remove_pos_from_leaf_node(resp_leaf, ret, n);
+      return UnlockAndReturn(locked_nodes, R_NONE_MOD);
+    } else {
+
+      if (unlikely(resp_leaf != leaf))
+        return UnlockAndReturn(locked_nodes, R_RETRY);
+      const uint64_t locked_version = leaf->lock();
+      if (unlikely(!btree::CheckVersion(version, locked_version))) {
+        leaf->unlock();
+        goto retry_cur_leaf;
+      }
+      locked_nodes.push_back(leaf);
+      if (old_v)
+        *old_v = leaf->values_[ret].v_;
+
+      uint64_t leaf_version = leaf->unstable_version();
+
+      leaf_node *left_sibling = AsLeaf(left_node);
+      leaf_node *right_sibling = AsLeaf(right_node);
+
+      // NOTE: remember that our locking discipline is left-to-right,
+      // bottom-to-top. Here, we must acquire all locks on nodes being
+      // modified in the tree. How we choose to handle removes is in the
+      // following preference:
+      //   1) steal from right node
+      //   2) merge with right node
+      //   3) steal from left node
+      //   4) merge with left node
+      //
+      // Btree invariants guarantee that at least one of the options above is
+      // available (except for the root node). We pick the right node first,
+      // because our locking discipline allows us to directly lock the right
+      // node.  If (1) and (2) cannot be satisfied, then we must first
+      // *unlock* the current node, lock the left node, relock the current
+      // node, and check nothing changed in between.
+
+      if (right_sibling) {
+        right_sibling->lock();
+        locked_nodes.push_back(right_sibling);
+      } else if (left_sibling) {
+        leaf->unlock();
+        left_sibling->lock();
+        locked_nodes.push_back(left_sibling);
+        leaf->lock();
+        if (unlikely(!leaf->check_version(leaf_version)))
+          return UnlockAndReturn(locked_nodes, R_RETRY);
+      } else {
+        INVARIANT(parents.empty());
+        if (unlikely(!leaf->is_root()))
+          return UnlockAndReturn(locked_nodes, R_RETRY);
+        //INVARIANT(leaf == root);
+      }
+
+      for (typename util::vec<remove_parent_entry>::type::reverse_iterator rit = parents.rbegin();
+           rit != parents.rend(); ++rit) {
+        node *p = rit->parent_;
+        node *l = rit->parent_left_sibling_;
+        node *r = rit->parent_right_sibling_;
+        uint64_t p_version = rit->parent_version_;
+        p->lock();
+        locked_nodes.push_back(p);
+        if (unlikely(!p->check_version(p_version)))
+          return UnlockAndReturn(locked_nodes, R_RETRY);
+        size_t p_n = p->key_slots_used();
+        if (p_n > NMinKeysPerNode)
+          break;
+        if (r) {
+          r->lock();
+          locked_nodes.push_back(r);
+        } else if (l) {
+          p->unlock();
+          l->lock();
+          locked_nodes.push_back(l);
+          p->lock();
+          if (unlikely(!p->check_version(p_version)))
+            return UnlockAndReturn(locked_nodes, R_RETRY);
+        } else {
+          if (unlikely(!p->is_root()))
+            return UnlockAndReturn(locked_nodes, R_RETRY);
+          //INVARIANT(p == root);
+        }
+      }
+
+      leaf->mark_modifying();
+
+      if (right_sibling) {
+        right_sibling->mark_modifying();
+        size_t right_n = right_sibling->key_slots_used();
+        if (right_n > NMinKeysPerNode) {
+          // steal first contiguous key slices from right
+          INVARIANT(right_sibling->keys_[0] > leaf->keys_[n - 1]);
+
+          // indices [0, steal_point) will be taken from the right
+          size_t steal_point = 1;
+          for (size_t i = 0; i < right_n - 1; i++, steal_point++)
+            if (likely(right_sibling->keys_[i] != right_sibling->keys_[steal_point]))
+              break;
+
+          INVARIANT(steal_point <= sizeof(key_slice) + 2);
+          INVARIANT(steal_point <= right_n);
+
+          // to steal, we need to ensure:
+          // 1) we have enough room to steal
+          // 2) the right sibling will not be empty after the steal
+          if ((n - 1 + steal_point) <= NKeysPerNode && steal_point < right_n) {
+            sift_left(leaf->keys_, ret, n);
+            copy_into(&leaf->keys_[n - 1], right_sibling->keys_, 0, steal_point);
+            sift_left(leaf->values_, ret, n);
+            copy_into(&leaf->values_[n - 1], right_sibling->values_, 0, steal_point);
+            sift_left(leaf->lengths_, ret, n);
+            copy_into(&leaf->lengths_[n - 1], right_sibling->lengths_, 0, steal_point);
+            if (leaf->suffixes_)
+              sift_swap_left(leaf->suffixes_, ret, n);
+            if (right_sibling->suffixes_) {
+              leaf->ensure_suffixes();
+              swap_with(&leaf->suffixes_[n - 1], right_sibling->suffixes_, 0, steal_point);
+            }
+
+            sift_left(right_sibling->keys_, 0, right_n, steal_point);
+            sift_left(right_sibling->values_, 0, right_n, steal_point);
+            sift_left(right_sibling->lengths_, 0, right_n, steal_point);
+            if (right_sibling->suffixes_)
+              sift_swap_left(right_sibling->suffixes_, 0, right_n, steal_point);
+            leaf->set_key_slots_used(n - 1 + steal_point);
+            right_sibling->set_key_slots_used(right_n - steal_point);
+            new_key = right_sibling->keys_[0];
+            right_sibling->min_key_ = new_key;
+
+#ifdef CHECK_INVARIANTS
+            leaf->base_invariant_unique_keys_check();
+            right_sibling->base_invariant_unique_keys_check();
+            INVARIANT(leaf->keys_[n - 1 + steal_point - 1] < new_key);
+#endif /* CHECK_INVARIANTS */
+
+            return R_STOLE_FROM_RIGHT;
+          } else {
+            // can't steal, so try merging- but only merge if we have room,
+            // otherwise just allow this node to have less elements
+            if ((n - 1 + right_n) > NKeysPerNode) {
+              INVARIANT(n > 1); // if we can't steal or merge, we must have
+                                // enough elements to just remove one w/o going empty
+              remove_pos_from_leaf_node(leaf, ret, n);
+              return UnlockAndReturn(locked_nodes, R_NONE_MOD);
+            }
+          }
+        }
+
+        // merge right sibling into this node
+        INVARIANT(right_sibling->keys_[0] > leaf->keys_[n - 1]);
+        INVARIANT((right_n + (n - 1)) <= NKeysPerNode);
+
+        sift_left(leaf->keys_, ret, n);
+        copy_into(&leaf->keys_[n - 1], right_sibling->keys_, 0, right_n);
+
+        sift_left(leaf->values_, ret, n);
+        copy_into(&leaf->values_[n - 1], right_sibling->values_, 0, right_n);
+
+        sift_left(leaf->lengths_, ret, n);
+        copy_into(&leaf->lengths_[n - 1], right_sibling->lengths_, 0, right_n);
+
+        if (leaf->suffixes_)
+          sift_swap_left(leaf->suffixes_, ret, n);
+
+        if (right_sibling->suffixes_) {
+          leaf->ensure_suffixes();
+          swap_with(&leaf->suffixes_[n - 1], right_sibling->suffixes_, 0, right_n);
+        }
+
+        leaf->set_key_slots_used(right_n + (n - 1));
+        leaf->next_ = right_sibling->next_;
+        if (right_sibling->next_)
+          right_sibling->next_->prev_ = leaf;
+
+        // leaf->next_->prev won't change because we hold lock for both leaf
+        // and right_sibling
+        INVARIANT(!leaf->next_ || leaf->next_->prev_ == leaf);
+
+        // leaf->prev_->next might change, however, since the left node could be
+        // splitting (and we might hold a pointer to the left-split of the left node,
+        // before it gets updated)
+        SINGLE_THREADED_INVARIANT(!leaf->prev_ || leaf->prev_->next_ == leaf);
+
+//#ifdef CHECK_INVARIANTS
+//        leaf->base_invariant_unique_keys_check();
+//#endif
+        leaf_node::release(right_sibling);
+        return R_MERGE_WITH_RIGHT;
+      }
+
+      if (left_sibling) {
+        left_sibling->mark_modifying();
+        size_t left_n = left_sibling->key_slots_used();
+        if (left_n > NMinKeysPerNode) {
+          // try to steal from left
+          INVARIANT(left_sibling->keys_[left_n - 1] < leaf->keys_[0]);
+
+          // indices [steal_point, left_n) will be taken from the left
+          size_t steal_point = left_n - 1;
+          for (ssize_t i = steal_point - 1; i >= 0; i--, steal_point--)
+            if (likely(left_sibling->keys_[i] != left_sibling->keys_[steal_point]))
+              break;
+
+          size_t nstolen = left_n - steal_point;
+          INVARIANT(nstolen <= sizeof(key_slice) + 2);
+          INVARIANT(steal_point < left_n);
+
+          if ((n - 1 + nstolen) <= NKeysPerNode && steal_point > 0) {
+            sift_right(leaf->keys_, ret + 1, n, nstolen - 1);
+            sift_right(leaf->keys_, 0, ret, nstolen);
+            copy_into(&leaf->keys_[0], &left_sibling->keys_[0], left_n - nstolen, left_n);
+
+            sift_right(leaf->values_, ret + 1, n, nstolen - 1);
+            sift_right(leaf->values_, 0, ret, nstolen);
+            copy_into(&leaf->values_[0], &left_sibling->values_[0], left_n - nstolen, left_n);
+
+            sift_right(leaf->lengths_, ret + 1, n, nstolen - 1);
+            sift_right(leaf->lengths_, 0, ret, nstolen);
+            copy_into(&leaf->lengths_[0], &left_sibling->lengths_[0], left_n - nstolen, left_n);
+
+            if (leaf->suffixes_) {
+              sift_swap_right(leaf->suffixes_, ret + 1, n, nstolen - 1);
+              sift_swap_right(leaf->suffixes_, 0, ret, nstolen);
+            }
+            if (left_sibling->suffixes_) {
+              leaf->ensure_suffixes();
+              swap_with(&leaf->suffixes_[0], &left_sibling->suffixes_[0], left_n - nstolen, left_n);
+            }
+
+            left_sibling->set_key_slots_used(left_n - nstolen);
+            leaf->set_key_slots_used(n - 1 + nstolen);
+            new_key = leaf->keys_[0];
+            leaf->min_key_ = new_key;
+
+#ifdef CHECK_INVARIANTS
+            leaf->base_invariant_unique_keys_check();
+            left_sibling->base_invariant_unique_keys_check();
+            INVARIANT(left_sibling->keys_[left_n - nstolen - 1] < new_key);
+#endif /* CHECK_INVARIANTS */
+
+            return R_STOLE_FROM_LEFT;
+          } else {
+            if ((left_n + (n - 1)) > NKeysPerNode) {
+              INVARIANT(n > 1);
+              remove_pos_from_leaf_node(leaf, ret, n);
+              return UnlockAndReturn(locked_nodes, R_NONE_MOD);
+            }
+          }
+        }
+
+        // merge this node into left sibling
+        INVARIANT(left_sibling->keys_[left_n - 1] < leaf->keys_[0]);
+        INVARIANT((left_n + (n - 1)) <= NKeysPerNode);
+
+        copy_into(&left_sibling->keys_[left_n], leaf->keys_, 0, ret);
+        copy_into(&left_sibling->keys_[left_n + ret], leaf->keys_, ret + 1, n);
+
+        copy_into(&left_sibling->values_[left_n], leaf->values_, 0, ret);
+        copy_into(&left_sibling->values_[left_n + ret], leaf->values_, ret + 1, n);
+
+        copy_into(&left_sibling->lengths_[left_n], leaf->lengths_, 0, ret);
+        copy_into(&left_sibling->lengths_[left_n + ret], leaf->lengths_, ret + 1, n);
+
+        if (leaf->suffixes_) {
+          left_sibling->ensure_suffixes();
+          swap_with(&left_sibling->suffixes_[left_n], leaf->suffixes_, 0, ret);
+          swap_with(&left_sibling->suffixes_[left_n + ret], leaf->suffixes_, ret + 1, n);
+        }
+
+        left_sibling->set_key_slots_used(left_n + (n - 1));
+        left_sibling->next_ = leaf->next_;
+        if (leaf->next_)
+          leaf->next_->prev_ = left_sibling;
+
+        // see comments in right_sibling case above, for why one of them is INVARIANT and
+        // the other is SINGLE_THREADED_INVARIANT
+        INVARIANT(!left_sibling->next_ || left_sibling->next_->prev_ == left_sibling);
+        SINGLE_THREADED_INVARIANT(
+            !left_sibling->prev_ ||
+            left_sibling->prev_->next_ == left_sibling);
+
+        //left_sibling->base_invariant_unique_keys_check();
+        leaf_node::release(leaf);
+        return R_MERGE_WITH_LEFT;
+      }
+
+      // root node, so we are ok
+      //INVARIANT(leaf == root);
+      INVARIANT(leaf->is_root());
+      remove_pos_from_leaf_node(leaf, ret, n);
+      return UnlockAndReturn(locked_nodes, R_NONE_MOD);
+    }
+  } else {
+    internal_node *internal = AsInternal(np);
+    uint64_t version = internal->stable_version();
+    if (unlikely(RawVersionManip::IsDeleting(version)))
+      return UnlockAndReturn(locked_nodes, R_RETRY);
+    key_search_ret kret = internal->key_lower_bound_search(kslice);
+    ssize_t ret = kret.first;
+    size_t n = kret.second;
+    size_t child_idx = (ret == -1) ? 0 : ret + 1;
+    node *child_ptr = internal->children_[child_idx];
+    key_slice *child_min_key = child_idx == 0 ? NULL : &internal->keys_[child_idx - 1];
+    key_slice *child_max_key = child_idx == n ? NULL : &internal->keys_[child_idx];
+    node *child_left_sibling = child_idx == 0 ? NULL : internal->children_[child_idx - 1];
+    node *child_right_sibling = child_idx == n ? NULL : internal->children_[child_idx + 1];
+    if (unlikely(!internal->check_version(version)))
+      return UnlockAndReturn(locked_nodes, R_RETRY);
+    parents.push_back(remove_parent_entry(internal, left_node, right_node, version));
+    INVARIANT(n > 0);
+    key_slice nk;
+    node *rn;
+    remove_status status = remove0(child_ptr,
+        child_min_key,
+        child_max_key,
+        k,
+        old_v,
+        child_left_sibling,
+        child_right_sibling,
+        nk,
+        rn,
+        parents,
+        locked_nodes);
+    switch (status) {
+      case R_NONE_NOMOD:
+      case R_NONE_MOD:
+      case R_RETRY:
+        return status;
+
+      case R_STOLE_FROM_LEFT:
+        INVARIANT(internal->is_locked());
+        INVARIANT(internal->is_lock_owner());
+        internal->keys_[child_idx - 1] = nk;
+        return UnlockAndReturn(locked_nodes, R_NONE_MOD);
+
+      case R_STOLE_FROM_RIGHT:
+        INVARIANT(internal->is_locked());
+        INVARIANT(internal->is_lock_owner());
+        internal->keys_[child_idx] = nk;
+        return UnlockAndReturn(locked_nodes, R_NONE_MOD);
+
+      case R_MERGE_WITH_LEFT:
+      case R_MERGE_WITH_RIGHT:
+        {
+          internal->mark_modifying();
+
+          size_t del_key_idx, del_child_idx;
+          if (status == R_MERGE_WITH_LEFT) {
+            // need to delete key at position (child_idx - 1), and child at
+            // position (child_idx)
+            del_key_idx = child_idx - 1;
+            del_child_idx = child_idx;
+          } else {
+            // need to delete key at position (child_idx), and child at
+            // posiiton (child_idx + 1)
+            del_key_idx = child_idx;
+            del_child_idx = child_idx + 1;
+          }
+
+          if (n > NMinKeysPerNode) {
+            remove_pos_from_internal_node(internal, del_key_idx, del_child_idx, n);
+            return UnlockAndReturn(locked_nodes, R_NONE_MOD);
+          }
+
+          internal_node *left_sibling = AsInternal(left_node);
+          internal_node *right_sibling = AsInternal(right_node);
+
+          // WARNING: if you change the order of events here, then you must also
+          // change the locking protocol (see the comment in the leaf node case)
+
+          if (right_sibling) {
+            right_sibling->mark_modifying();
+            size_t right_n = right_sibling->key_slots_used();
+            INVARIANT(max_key);
+            INVARIANT(right_sibling->keys_[0] > internal->keys_[n - 1]);
+            INVARIANT(*max_key > internal->keys_[n - 1]);
+            if (right_n > NMinKeysPerNode) {
+              // steal from right
+              sift_left(internal->keys_, del_key_idx, n);
+              internal->keys_[n - 1] = *max_key;
+
+              sift_left(internal->children_, del_child_idx, n + 1);
+              internal->children_[n] = right_sibling->children_[0];
+
+              new_key = right_sibling->keys_[0];
+
+              sift_left(right_sibling->keys_, 0, right_n);
+              sift_left(right_sibling->children_, 0, right_n + 1);
+              right_sibling->dec_key_slots_used();
+
+              return R_STOLE_FROM_RIGHT;
+            } else {
+              // merge with right
+              INVARIANT(max_key);
+
+              sift_left(internal->keys_, del_key_idx, n);
+              internal->keys_[n - 1] = *max_key;
+              copy_into(&internal->keys_[n], right_sibling->keys_, 0, right_n);
+
+              sift_left(internal->children_, del_child_idx, n + 1);
+              copy_into(&internal->children_[n], right_sibling->children_, 0, right_n + 1);
+
+              internal->set_key_slots_used(n + right_n);
+              internal_node::release(right_sibling);
+              return R_MERGE_WITH_RIGHT;
+            }
+          }
+
+          if (left_sibling) {
+            left_sibling->mark_modifying();
+            size_t left_n = left_sibling->key_slots_used();
+            INVARIANT(min_key);
+            INVARIANT(left_sibling->keys_[left_n - 1] < internal->keys_[0]);
+            INVARIANT(left_sibling->keys_[left_n - 1] < *min_key);
+            INVARIANT(*min_key < internal->keys_[0]);
+            if (left_n > NMinKeysPerNode) {
+              // steal from left
+              sift_right(internal->keys_, 0, del_key_idx);
+              internal->keys_[0] = *min_key;
+
+              sift_right(internal->children_, 0, del_child_idx);
+              internal->children_[0] = left_sibling->children_[left_n];
+
+              new_key = left_sibling->keys_[left_n - 1];
+              left_sibling->dec_key_slots_used();
+
+              return R_STOLE_FROM_LEFT;
+            } else {
+              // merge into left sibling
+              INVARIANT(min_key);
+
+              size_t left_key_j = left_n;
+              size_t left_child_j = left_n + 1;
+
+              left_sibling->keys_[left_key_j++] = *min_key;
+
+              copy_into(&left_sibling->keys_[left_key_j], internal->keys_, 0, del_key_idx);
+              left_key_j += del_key_idx;
+              copy_into(&left_sibling->keys_[left_key_j], internal->keys_, del_key_idx + 1, n);
+
+              copy_into(&left_sibling->children_[left_child_j], internal->children_, 0, del_child_idx);
+              left_child_j += del_child_idx;
+              copy_into(&left_sibling->children_[left_child_j], internal->children_, del_child_idx + 1, n + 1);
+
+              left_sibling->set_key_slots_used(n + left_n);
+              internal_node::release(internal);
+              return R_MERGE_WITH_LEFT;
+            }
+          }
+
+          //INVARIANT(internal == root);
+          INVARIANT(internal->is_root());
+          remove_pos_from_internal_node(internal, del_key_idx, del_child_idx, n);
+          INVARIANT(internal->key_slots_used() + 1 == n);
+          if ((n - 1) == 0) {
+            replace_node = internal->children_[0];
+            internal_node::release(internal);
+            return R_REPLACE_NODE;
+          }
+
+          return UnlockAndReturn(locked_nodes, R_NONE_MOD);
+        }
+
+      default:
+        ALWAYS_ASSERT(false);
+        return UnlockAndReturn(locked_nodes, R_NONE_NOMOD);
+    }
+  }
+}
+
+template <typename P>
+std::string
+btree<P>::NodeStringify(const node_opaque_t *n)
+{
+  std::vector<std::string> keys;
+  for (size_t i = 0; i < n->key_slots_used(); i++)
+    keys.push_back(std::string("0x") + util::hexify(n->keys_[i]));
+
+  std::ostringstream b;
+  b << "node[v=" << n->version_info_str()
+    << ", keys=" << util::format_list(keys.begin(), keys.end());
+
+  if (n->is_leaf_node()) {
+    const leaf_node *leaf = AsLeaf(n);
+    std::vector<std::string> lengths;
+    for (size_t i = 0; i < leaf->key_slots_used(); i++) {
+      std::ostringstream inf;
+      inf << "<l=" << leaf->keyslice_length(i) << ",is_layer=" << leaf->is_layer(i) << ">";
+      lengths.push_back(inf.str());
+    }
+    b << ", lengths=" << util::format_list(lengths.begin(), lengths.end());
+  } else {
+    //const internal_node *internal = AsInternal(n);
+    // nothing for now
+  }
+
+  b << "]";
+  return b.str();
+}
+
+template <typename P>
+std::vector<std::pair<typename btree<P>::value_type, bool>>
+btree<P>::ExtractValues(const node_opaque_t *n)
+{
+  std::vector< std::pair<value_type, bool> > ret;
+  if (!n->is_leaf_node())
+    return ret;
+  const leaf_node *leaf = (const leaf_node *) n;
+  const size_t sz = leaf->key_slots_used();
+  for (size_t i = 0; i < sz; i++)
+    if (!leaf->is_layer(i))
+      ret.emplace_back(leaf->values_[i].v_, leaf->keyslice_length(i) > 8);
+  return ret;
+}
diff --git a/silo/circbuf.h b/silo/circbuf.h
new file mode 100644 (file)
index 0000000..a6be935
--- /dev/null
@@ -0,0 +1,112 @@
+#pragma once
+
+#include <cstring>
+#include <atomic>
+#include <vector>
+#include <limits>
+
+#include "macros.h"
+#include "amd64.h"
+
+// Thread safety is ensured for many concurrent enqueuers but only one
+// concurrent dequeuer. That is, the head end is thread safe, but the tail end
+// can only be manipulated by a single thread.
+template <typename Tp, unsigned int Capacity>
+class circbuf {
+public:
+  circbuf()
+    : head_(0), tail_(0)
+  {
+    memset(&buf_[0], 0, Capacity * sizeof(buf_[0]));
+  }
+
+  inline bool
+  empty() const
+  {
+    return head_.load(std::memory_order_acquire) ==
+           tail_.load(std::memory_order_acquire) &&
+           !buf_[head_.load(std::memory_order_acquire)].load(std::memory_order_acquire);
+  }
+
+  // blocks until something enqs()
+  inline void
+  enq(Tp *p)
+  {
+    INVARIANT(p);
+
+  retry:
+    unsigned icur = head_.load(std::memory_order_acquire);
+    INVARIANT(icur < Capacity);
+    if (buf_[icur].load(std::memory_order_acquire)) {
+      nop_pause();
+      goto retry;
+    }
+
+    // found an empty spot, so we now race for it
+    unsigned inext = (icur + 1) % Capacity;
+    if (!head_.compare_exchange_strong(icur, inext, std::memory_order_acq_rel)) {
+      nop_pause();
+      goto retry;
+    }
+
+    INVARIANT(!buf_[icur].load(std::memory_order_acquire));
+    buf_[icur].store(p, std::memory_order_release);
+  }
+
+  // blocks until something deqs()
+  inline Tp *
+  deq()
+  {
+    while (!buf_[tail_.load(std::memory_order_acquire)].load(std::memory_order_acquire))
+      nop_pause();
+    Tp *ret = buf_[tail_.load(std::memory_order_acquire)].load(std::memory_order_acquire);
+    buf_[postincr(tail_)].store(nullptr, std::memory_order_release);
+    INVARIANT(ret);
+    return ret;
+  }
+
+  inline Tp *
+  peek()
+  {
+    return buf_[tail_.load(std::memory_order_acquire)].load(std::memory_order_acquire);
+  }
+
+  // takes a current snapshot of all entries in the queue
+  inline void
+  peekall(std::vector<Tp *> &ps, size_t limit = std::numeric_limits<size_t>::max())
+  {
+    ps.clear();
+    const unsigned t = tail_.load(std::memory_order_acquire);
+    unsigned i = t;
+    Tp *p;
+    while ((p = buf_[i].load(std::memory_order_acquire)) && ps.size() < limit) {
+      ps.push_back(p);
+      postincr(i);
+      if (i == t)
+        // have fully wrapped around
+        break;
+    }
+  }
+
+private:
+
+  static inline unsigned
+  postincr(unsigned &i)
+  {
+    const unsigned ret = i;
+    i = (i + 1) % Capacity;
+    return ret;
+  }
+
+  static inline unsigned
+  postincr(std::atomic<unsigned> &i)
+  {
+    const unsigned ret = i.load(std::memory_order_acquire);
+    i.store((ret + 1) % Capacity, std::memory_order_release);
+    return ret;
+  }
+
+  std::atomic<Tp *> buf_[Capacity];
+  std::atomic<unsigned> head_;
+  std::atomic<unsigned> tail_;
+};
diff --git a/silo/compile.sh b/silo/compile.sh
new file mode 100755 (executable)
index 0000000..f82223c
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/bash
+MODE=perf CHECK_INVARIANTS=1 make -j
+MODE=perf CHECK_INVARIANTS=1 make -j dbtest
+
diff --git a/silo/config/config-backoff.h b/silo/config/config-backoff.h
new file mode 100644 (file)
index 0000000..aac2f81
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+#define SPINLOCK_BACKOFF
diff --git a/silo/config/config-factor-fake-compression.h b/silo/config/config-factor-fake-compression.h
new file mode 100644 (file)
index 0000000..473f687
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+#define LOGGER_UNSAFE_FAKE_COMPRESSION
diff --git a/silo/config/config-factor-gc-nowriteinplace.h b/silo/config/config-factor-gc-nowriteinplace.h
new file mode 100644 (file)
index 0000000..1fddf3f
--- /dev/null
@@ -0,0 +1,5 @@
+#pragma once
+
+#define PROTO2_CAN_DISABLE_GC
+#define PROTO2_CAN_DISABLE_SNAPSHOTS
+#define DISABLE_OVERWRITE_IN_PLACE
diff --git a/silo/config/config-factor-gc.h b/silo/config/config-factor-gc.h
new file mode 100644 (file)
index 0000000..cd3d995
--- /dev/null
@@ -0,0 +1,4 @@
+#pragma once
+
+#define PROTO2_CAN_DISABLE_GC
+#define PROTO2_CAN_DISABLE_SNAPSHOTS
diff --git a/silo/config/config-perf.h b/silo/config/config-perf.h
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/silo/config/config-sandbox.h b/silo/config/config-sandbox.h
new file mode 100644 (file)
index 0000000..f63149d
--- /dev/null
@@ -0,0 +1,6 @@
+#pragma once
+
+/**
+ * just for debugging purposes -
+ * sandbox builds should never be used for any performance testing
+ */
diff --git a/silo/core.cc b/silo/core.cc
new file mode 100644 (file)
index 0000000..8f91fde
--- /dev/null
@@ -0,0 +1,35 @@
+#include <unistd.h>
+
+#include "amd64.h"
+#include "core.h"
+#include "util.h"
+
+using namespace std;
+using namespace util;
+
+int
+coreid::allocate_contiguous_aligned_block(unsigned n, unsigned alignment)
+{
+retry:
+  unsigned current = g_core_count.load(memory_order_acquire);
+  const unsigned rounded = slow_round_up(current, alignment);
+  const unsigned replace = rounded + n;
+  if (unlikely(replace > NMaxCores))
+    return -1;
+  if (!g_core_count.compare_exchange_strong(current, replace, memory_order_acq_rel)) {
+    nop_pause();
+    goto retry;
+  }
+  return rounded;
+}
+
+unsigned
+coreid::num_cpus_online()
+{
+  const long nprocs = sysconf(_SC_NPROCESSORS_ONLN);
+  ALWAYS_ASSERT(nprocs >= 1);
+  return nprocs;
+}
+
+__thread int coreid::tl_core_id = -1;
+atomic<unsigned> coreid::g_core_count(0);
diff --git a/silo/core.h b/silo/core.h
new file mode 100644 (file)
index 0000000..1750ee3
--- /dev/null
@@ -0,0 +1,206 @@
+#pragma once
+
+#include <atomic>
+#include <sys/types.h>
+#include "macros.h"
+#include "util.h"
+
+/**
+ * XXX: CoreIDs are not recyclable for now, so NMAXCORES is really the number
+ * of threads which can ever be spawned in the system
+ */
+class coreid {
+public:
+  static const unsigned NMaxCores = NMAXCORES;
+
+  static inline unsigned
+  core_id()
+  {
+    if (unlikely(tl_core_id == -1)) {
+      // initialize per-core data structures
+      tl_core_id = g_core_count.fetch_add(1, std::memory_order_acq_rel);
+      // did we exceed max cores?
+      ALWAYS_ASSERT(unsigned(tl_core_id) < NMaxCores);
+    }
+    return tl_core_id;
+  }
+
+  /**
+   * Since our current allocation scheme does not allow for holes in the
+   * allocation, this function is quite wasteful. Don't abuse.
+   *
+   * Returns -1 if it is impossible to do this w/o exceeding max allocations
+   */
+  static int
+  allocate_contiguous_aligned_block(unsigned n, unsigned alignment);
+
+  /**
+   * WARNING: this function is scary, and exists solely as a hack
+   *
+   * You are allowed to set your own core id under several conditions
+   * (the idea is that somebody else has allocated a block of core ids
+   *  and is assigning one to you, under the promise of uniqueness):
+   *
+   * 1) You haven't already called core_id() yet (so you have no assignment)
+   * 2) The number you are setting is < the current assignment counter (meaning
+   *    it was previously assigned by someone)
+   *
+   * These are necessary but not sufficient conditions for uniqueness
+   */
+  static void
+  set_core_id(unsigned cid)
+  {
+    ALWAYS_ASSERT(cid < NMaxCores);
+    ALWAYS_ASSERT(cid < g_core_count.load(std::memory_order_acquire));
+    ALWAYS_ASSERT(tl_core_id == -1);
+    tl_core_id = cid; // sigh
+  }
+
+  // actual number of CPUs online for the system
+  static unsigned num_cpus_online();
+
+private:
+  // the core ID of this core: -1 if not set
+  static __thread int tl_core_id;
+
+  // contains a running count of all the cores
+  static std::atomic<unsigned> g_core_count CACHE_ALIGNED;
+};
+
+// requires T to have no-arg ctor
+template <typename T, bool CallDtor = false, bool Pedantic = true>
+class percore {
+public:
+
+  percore()
+  {
+    for (size_t i = 0; i < size(); i++) {
+      using namespace util;
+      new (&(elems()[i])) aligned_padded_elem<T, Pedantic>();
+    }
+  }
+
+  ~percore()
+  {
+    if (!CallDtor)
+      return;
+    for (size_t i = 0; i < size(); i++) {
+      using namespace util;
+      elems()[i].~aligned_padded_elem<T, Pedantic>();
+    }
+  }
+
+  inline T &
+  operator[](unsigned i)
+  {
+    INVARIANT(i < NMAXCORES);
+    return elems()[i].elem;
+  }
+
+  inline const T &
+  operator[](unsigned i) const
+  {
+    INVARIANT(i < NMAXCORES);
+    return elems()[i].elem;
+  }
+
+  inline T &
+  my()
+  {
+    return (*this)[coreid::core_id()];
+  }
+
+  inline const T &
+  my() const
+  {
+    return (*this)[coreid::core_id()];
+  }
+
+  // XXX: make an iterator
+
+  inline size_t
+  size() const
+  {
+    return NMAXCORES;
+  }
+
+protected:
+
+  inline util::aligned_padded_elem<T, Pedantic> *
+  elems()
+  {
+    return (util::aligned_padded_elem<T, Pedantic> *) &bytes_[0];
+  }
+
+  inline const util::aligned_padded_elem<T, Pedantic> *
+  elems() const
+  {
+    return (const util::aligned_padded_elem<T, Pedantic> *) &bytes_[0];
+  }
+
+  char bytes_[sizeof(util::aligned_padded_elem<T, Pedantic>) * NMAXCORES];
+};
+
+namespace private_ {
+  template <typename T>
+  struct buf {
+    char bytes_[sizeof(T)];
+    inline T * cast() { return (T *) &bytes_[0]; }
+    inline const T * cast() const { return (T *) &bytes_[0]; }
+  };
+}
+
+template <typename T>
+class percore_lazy : private percore<private_::buf<T>, false> {
+  typedef private_::buf<T> buf_t;
+public:
+
+  percore_lazy()
+  {
+    NDB_MEMSET(&flags_[0], 0, sizeof(flags_));
+  }
+
+  template <class... Args>
+  inline T &
+  get(unsigned i, Args &&... args)
+  {
+    buf_t &b = this->elems()[i].elem;
+    if (unlikely(!flags_[i])) {
+      flags_[i] = true;
+      T *px = new (&b.bytes_[0]) T(std::forward<Args>(args)...);
+      return *px;
+    }
+    return *b.cast();
+  }
+
+  template <class... Args>
+  inline T &
+  my(Args &&... args)
+  {
+    return get(coreid::core_id(), std::forward<Args>(args)...);
+  }
+
+  inline T *
+  view(unsigned i)
+  {
+    buf_t &b = this->elems()[i].elem;
+    return flags_[i] ? b.cast() : nullptr;
+  }
+
+  inline const T *
+  view(unsigned i) const
+  {
+    const buf_t &b = this->elems()[i].elem;
+    return flags_[i] ? b.cast() : nullptr;
+  }
+
+  inline const T *
+  myview() const
+  {
+    return view(coreid::core_id());
+  }
+
+private:
+  bool flags_[NMAXCORES];
+  CACHE_PADOUT;
+};
diff --git a/silo/counter.cc b/silo/counter.cc
new file mode 100644 (file)
index 0000000..c930d65
--- /dev/null
@@ -0,0 +1,119 @@
+#include "counter.h"
+#include "util.h"
+#include "lockguard.h"
+
+using namespace std;
+using namespace util;
+using namespace private_;
+
+map<string, event_ctx *> &
+event_ctx::event_counters()
+{
+  static map<string, event_ctx *> s_counters;
+  return s_counters;
+}
+
+spinlock &
+event_ctx::event_counters_lock()
+{
+  static spinlock s_lock;
+  return s_lock;
+}
+
+void
+event_ctx::stat(counter_data &d)
+{
+  for (size_t i = 0; i < coreid::NMaxCores; i++)
+    d.count_ += counts_[i];
+  if (avg_tag_) {
+    d.type_ = counter_data::TYPE_AGG;
+    uint64_t m = 0;
+    for (size_t i = 0; i < coreid::NMaxCores; i++) {
+      m = max(m, static_cast<event_ctx_avg *>(this)->highs_[i]);
+    }
+    uint64_t s = 0;
+    for (size_t i = 0; i < coreid::NMaxCores; i++)
+      s += static_cast<event_ctx_avg *>(this)->sums_[i];
+    d.sum_ = s;
+    d.max_ = m;
+  }
+}
+
+map<string, counter_data>
+event_counter::get_all_counters()
+{
+  map<string, counter_data> ret;
+  const map<string, event_ctx *> &evts = event_ctx::event_counters();
+  spinlock &l = event_ctx::event_counters_lock();
+  lock_guard<spinlock> sl(l);
+  for (auto &p : evts) {
+    counter_data d;
+    p.second->stat(d);
+    if (d.type_ == counter_data::TYPE_AGG)
+      ret[p.first].type_ = counter_data::TYPE_AGG;
+    ret[p.first] += d;
+  }
+  return ret;
+}
+
+void
+event_counter::reset_all_counters()
+{
+  const map<string, event_ctx *> &evts = event_ctx::event_counters();
+  spinlock &l = event_ctx::event_counters_lock();
+  lock_guard<spinlock> sl(l);
+  for (auto &p : evts)
+    for (size_t i = 0; i < coreid::NMaxCores; i++) {
+      p.second->counts_[i] = 0;
+      if (p.second->avg_tag_) {
+        static_cast<event_ctx_avg *>(p.second)->sums_[i] = 0;
+        static_cast<event_ctx_avg *>(p.second)->highs_[i] = 0;
+      }
+    }
+}
+
+bool
+event_counter::stat(const string &name, counter_data &d)
+{
+  const map<string, event_ctx *> &evts = event_ctx::event_counters();
+  spinlock &l = event_ctx::event_counters_lock();
+  event_ctx *ctx = nullptr;
+  {
+    lock_guard<spinlock> sl(l);
+    auto it = evts.find(name);
+    if (it != evts.end())
+      ctx = it->second;
+  }
+  if (!ctx)
+    return false;
+  ctx->stat(d);
+  return true;
+}
+
+#ifdef ENABLE_EVENT_COUNTERS
+event_counter::event_counter(const string &name)
+  : ctx_(name, false)
+{
+  spinlock &l = event_ctx::event_counters_lock();
+  map<string, event_ctx *> &evts = event_ctx::event_counters();
+  lock_guard<spinlock> sl(l);
+  evts[name] = ctx_.obj();
+}
+
+event_avg_counter::event_avg_counter(const string &name)
+  : ctx_(name)
+{
+  spinlock &l = event_ctx::event_counters_lock();
+  map<string, event_ctx *> &evts = event_ctx::event_counters();
+  lock_guard<spinlock> sl(l);
+  evts[name] = ctx_.obj();
+}
+#else
+event_counter::event_counter(const string &name)
+{
+}
+
+event_avg_counter::event_avg_counter(const string &name)
+{
+}
+#endif
diff --git a/silo/counter.h b/silo/counter.h
new file mode 100644 (file)
index 0000000..94a8187
--- /dev/null
@@ -0,0 +1,163 @@
+#ifndef _COUNTER_H_
+#define _COUNTER_H_
+
+// system event counters, for
+
+#include <algorithm> // for std::max
+#include <vector>
+#include <map>
+#include <string>
+#include <stdint.h>
+
+#include "macros.h"
+#include "core.h"
+#include "util.h"
+#include "spinlock.h"
+
+struct counter_data {
+  enum Type { TYPE_COUNT, TYPE_AGG };
+
+  counter_data()
+    : type_(TYPE_COUNT), count_(0), sum_(0), max_(0) {}
+
+  Type type_;
+  uint64_t count_;
+  uint64_t sum_;
+  uint64_t max_;
+
+  inline counter_data &
+  operator+=(const counter_data &that)
+  {
+    count_ += that.count_;
+    sum_   += that.sum_;
+    max_    = std::max(max_, that.max_);
+    return *this;
+  }
+
+  inline double
+  avg() const
+  {
+    INVARIANT(type_ == TYPE_AGG);
+    return double(sum_)/double(count_);
+  }
+};
+
+namespace private_ {
+
+  // these objects are *never* supposed to be destructed
+  // (this is a purposeful memory leak)
+  struct event_ctx {
+
+    static std::map<std::string, event_ctx *> &event_counters();
+    static spinlock &event_counters_lock();
+
+    // tag to avoid making event_ctx virtual
+    event_ctx(const std::string &name, bool avg_tag)
+      : name_(name), avg_tag_(avg_tag)
+    {}
+
+    ~event_ctx()
+    {
+      ALWAYS_ASSERT(false);
+    }
+
+    event_ctx(const event_ctx &) = delete;
+    event_ctx &operator=(const event_ctx &) = delete;
+    event_ctx(event_ctx &&) = delete;
+
+    void stat(counter_data &d);
+
+    const std::string name_;
+    const bool avg_tag_;
+
+    // per-thread counts
+    percore<uint64_t, false, false> counts_;
+  };
+
+  // more expensive
+  struct event_ctx_avg : public event_ctx {
+    event_ctx_avg(const std::string &name) : event_ctx(name, true) {}
+    percore<uint64_t, false, false> sums_;
+    percore<uint64_t, false, false> highs_;
+  };
+}
+
+class event_counter {
+public:
+  event_counter(const std::string &name);
+
+  event_counter(const event_counter &) = delete;
+  event_counter &operator=(const event_counter &) = delete;
+  event_counter(event_counter &&) = delete;
+
+  inline ALWAYS_INLINE void
+  inc(uint64_t i = 1)
+  {
+#ifdef ENABLE_EVENT_COUNTERS
+    ctx_->counts_.my() += i;
+#endif
+  }
+
+  inline ALWAYS_INLINE event_counter &
+  operator++()
+  {
+    inc();
+    return *this;
+  }
+
+  inline ALWAYS_INLINE event_counter &
+  operator+=(uint64_t i)
+  {
+    inc(i);
+    return *this;
+  }
+
+  // WARNING: an expensive operation!
+  static std::map<std::string, counter_data> get_all_counters();
+  // WARNING: an expensive operation!
+  static void reset_all_counters();
+  // WARNING: an expensive operation!
+  static bool
+  stat(const std::string &name, counter_data &d);
+
+private:
+#ifdef ENABLE_EVENT_COUNTERS
+  unmanaged<private_::event_ctx> ctx_;
+#endif
+};
+
+class event_avg_counter {
+public:
+  event_avg_counter(const std::string &name);
+
+  event_avg_counter(const event_avg_counter &) = delete;
+  event_avg_counter &operator=(const event_avg_counter &) = delete;
+  event_avg_counter(event_avg_counter &&) = delete;
+
+  inline ALWAYS_INLINE void
+  offer(uint64_t value)
+  {
+#ifdef ENABLE_EVENT_COUNTERS
+    ctx_->counts_.my()++;
+    ctx_->sums_.my() += value;
+    ctx_->highs_.my() = std::max(ctx_->highs_.my(), value);
+#endif
+  }
+
+private:
+#ifdef ENABLE_EVENT_COUNTERS
+  unmanaged<private_::event_ctx_avg> ctx_;
+#endif
+};
+
+inline std::ostream &
+operator<<(std::ostream &o, const counter_data &d)
+{
+  if (d.type_ == counter_data::TYPE_COUNT)
+    o << "count=" << d.count_;
+  else
+    o << "count=" << d.count_ << ", max=" << d.max_ << ", avg=" << d.avg();
+  return o;
+}
+
+#endif /* _COUNTER_H_ */
diff --git a/silo/fileutils.h b/silo/fileutils.h
new file mode 100644 (file)
index 0000000..af0cbad
--- /dev/null
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <unistd.h>
+#include <errno.h>
+
+class fileutils {
+public:
+
+  static int
+  writeall(int fd, const char *buf, int n)
+  {
+    while (n) {
+      int r = write(fd, buf, n);
+      if (unlikely(r < 0))
+        return r;
+      buf += r;
+      n -= r;
+    }
+    return 0;
+  }
+
+  static int
+  readall(int fd, char *buf, int n)
+  {
+    while (n) {
+      int r = read(fd, buf, n);
+      if (r == 0)
+        return EOF;
+      if (r < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+          continue;
+        return r;
+      }
+      buf += r;
+      n -= r;
+    }
+    return 0;
+  }
+
+};
diff --git a/silo/imstring.h b/silo/imstring.h
new file mode 100644 (file)
index 0000000..f46834b
--- /dev/null
@@ -0,0 +1,125 @@
+#ifndef _NDB_IMSTRING_H_
+#define _NDB_IMSTRING_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+#include <limits>
+
+#include "macros.h"
+#include "rcu.h"
+#include "util.h"
+#include "counter.h"
+
+/**
+ * Not-really-immutable string, for perf reasons. Also can use
+ * RCU for GC
+ */
+template <bool RCU>
+class base_imstring {
+
+  template <bool R>
+  friend class base_imstring;
+
+  // we can't really support keys > 65536, but most DBs impose
+  // limits on keys
+  typedef uint16_t internal_size_type;
+
+  static inline ALWAYS_INLINE internal_size_type
+  CheckBounds(size_t l)
+  {
+    INVARIANT(l <= std::numeric_limits<internal_size_type>::max());
+    return l;
+  }
+
+  static event_counter g_evt_imstring_bytes_allocated;
+  static event_counter g_evt_imstring_bytes_freed;
+  static event_avg_counter g_evt_avg_imstring_len;
+
+public:
+  base_imstring() : p(NULL), l(0) {}
+
+  base_imstring(const uint8_t *src, size_t l)
+    : p(new uint8_t[l]), l(CheckBounds(l))
+  {
+    g_evt_imstring_bytes_allocated += l;
+    g_evt_avg_imstring_len.offer(l);
+    NDB_MEMCPY(p, src, l);
+  }
+
+  base_imstring(const std::string &s)
+    : p(new uint8_t[s.size()]), l(CheckBounds(s.size()))
+  {
+    g_evt_imstring_bytes_allocated += l;
+    g_evt_avg_imstring_len.offer(l);
+    NDB_MEMCPY(p, s.data(), l);
+  }
+
+  base_imstring(const base_imstring &) = delete;
+  base_imstring(base_imstring &&) = delete;
+  base_imstring &operator=(const base_imstring &) = delete;
+
+  template <bool R>
+  inline void
+  swap(base_imstring<R> &that)
+  {
+    // std::swap() doesn't work for packed elems
+    uint8_t * const temp_p = p;
+    p = that.p;
+    that.p = temp_p;
+    internal_size_type const temp_l = l;
+    l = that.l;
+    that.l = temp_l;
+  }
+
+  inline
+  ~base_imstring()
+  {
+    release();
+    g_evt_imstring_bytes_freed += l;
+  }
+
+  inline const uint8_t *
+  data() const
+  {
+    return p;
+  }
+
+  inline size_t
+  size() const
+  {
+    return l;
+  }
+
+private:
+
+  inline void
+  release()
+  {
+    if (likely(p)) {
+      if (RCU)
+        rcu::s_instance.free_array(p);
+      else
+        delete [] p;
+    }
+  }
+
+  uint8_t *p;
+  internal_size_type l;
+} PACKED;
+
+template <bool RCU>
+event_counter base_imstring<RCU>::g_evt_imstring_bytes_allocated("imstring_bytes_allocated");
+
+template <bool RCU>
+event_counter base_imstring<RCU>::g_evt_imstring_bytes_freed("imstring_bytes_freed");
+
+template <bool RCU>
+event_avg_counter base_imstring<RCU>::g_evt_avg_imstring_len("avg_imstring_len");
+
+typedef base_imstring<false> imstring;
+typedef base_imstring<true>  rcu_imstring;
+
+#endif /* _NDB_IMSTRING_H_ */
diff --git a/silo/lockguard.h b/silo/lockguard.h
new file mode 100644 (file)
index 0000000..71e5f9f
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _LOCK_GUARD_H_
+#define _LOCK_GUARD_H_
+
+#include <utility>
+
+#include "macros.h"
+
+// this exists in C++11, but we have a richer constructor
+template <typename BasicLockable>
+class lock_guard {
+public:
+
+  template <class... Args>
+  lock_guard(BasicLockable *l, Args &&... args)
+    : l(l)
+  {
+    if (likely(l))
+      l->lock(std::forward<Args>(args)...);
+  }
+
+  template <class... Args>
+  lock_guard(BasicLockable &l, Args &&... args)
+    : l(&l)
+  {
+    l.lock(std::forward<Args>(args)...);
+  }
+
+  ~lock_guard()
+  {
+    if (likely(l))
+      l->unlock();
+  }
+private:
+  BasicLockable *l;
+};
+
+#endif /* _LOCK_GUARD_H_ */
diff --git a/silo/log2.hh b/silo/log2.hh
new file mode 100644 (file)
index 0000000..5264f36
--- /dev/null
@@ -0,0 +1,42 @@
+// taken from xv6
+
+#pragma once
+
+#include <cstddef>
+
+// Return ceil(log2(x)).
+static inline std::size_t
+ceil_log2(std::size_t x)
+{
+  auto bits = sizeof(long long) * 8 - __builtin_clzll(x);
+  if (x == (std::size_t)1 << (bits - 1))
+    return bits - 1;
+  return bits;
+}
+
+// Return ceil(log2(x)).  This is slow, but can be evaluated in a
+// constexpr context.  'exact' is used internally and should not be
+// provided by the caller.
+static inline constexpr std::size_t
+ceil_log2_const(std::size_t x, bool exact = true)
+{
+  return (x == 0) ? (1/x)
+    : (x == 1) ? (exact ? 0 : 1)
+    : 1 + ceil_log2_const(x >> 1, ((x & 1) == 1) ? false : exact);
+}
+
+// Round up to the nearest power of 2
+static inline std::size_t
+round_up_to_pow2(std::size_t x)
+{
+  auto bits = sizeof(long long) * 8 - __builtin_clzll(x);
+  if (x == (std::size_t)1 << (bits - 1))
+    return x;
+  return (std::size_t)1 << bits;
+}
+
+static inline constexpr std::size_t
+round_up_to_pow2_const(std::size_t x)
+{
+  return (std::size_t)1 << ceil_log2_const(x);
+}
diff --git a/silo/macros.h b/silo/macros.h
new file mode 100644 (file)
index 0000000..2b26092
--- /dev/null
@@ -0,0 +1,132 @@
+#ifndef _MACROS_H_
+#define _MACROS_H_
+
+#include <assert.h>
+#include <stdexcept>
+
+/** options */
+//#define TUPLE_PREFETCH
+#define BTREE_NODE_PREFETCH
+//#define DIE_ON_ABORT
+//#define TRAP_LARGE_ALLOOCATIONS
+#define USE_BUILTIN_MEMFUNCS
+//#define CHECK_INVARIANTS
+//#define TUPLE_CHECK_KEY
+#define USE_SMALL_CONTAINER_OPT
+#define BTREE_NODE_ALLOC_CACHE_ALIGNED
+#define TXN_BTREE_DUMP_PURGE_STATS
+//#define ENABLE_EVENT_COUNTERS
+//#define ENABLE_BENCH_TXN_COUNTERS
+#define USE_VARINT_ENCODING
+//#define DISABLE_FIELD_SELECTION
+//#define PARANOID_CHECKING
+//#define BTREE_LOCK_OWNERSHIP_CHECKING
+//#define TUPLE_LOCK_OWNERSHIP_CHECKING
+//#define MEMCHECK_MAGIC 0xFF
+//#define TUPLE_MAGIC
+//#define PROTO2_CAN_DISABLE_GC
+//#define PROTO2_CAN_DISABLE_SNAPSHOTS
+//#define USE_PERF_CTRS
+
+#ifndef CONFIG_H
+#error "no CONFIG_H set"
+#endif
+
+#include CONFIG_H
+
+/**
+ * some non-sensical options, which only make sense for performance debugging
+ * experiments. these should ALL be DISABLED when doing actual benchmarking
+ **/
+//#define LOGGER_UNSAFE_FAKE_COMPRESSION
+//#define LOGGER_UNSAFE_REDUCE_BUFFER_SIZE
+//#define LOGGER_STRIDE_OVER_BUFFER
+
+#define CACHELINE_SIZE 64 // XXX: don't assume x86
+#define LG_CACHELINE_SIZE __builtin_ctz(CACHELINE_SIZE)
+
+// global maximum on the number of unique threads allowed
+// in the system
+#define NMAXCOREBITS 9
+#define NMAXCORES    (1 << NMAXCOREBITS)
+
+// some helpers for cacheline alignment
+#define CACHE_ALIGNED __attribute__((aligned(CACHELINE_SIZE)))
+
+#define __XCONCAT2(a, b) a ## b
+#define __XCONCAT(a, b) __XCONCAT2(a, b)
+#define CACHE_PADOUT  \
+    char __XCONCAT(__padout, __COUNTER__)[0] __attribute__((aligned(CACHELINE_SIZE)))
+#define PACKED __attribute__((packed))
+
+#define NEVER_INLINE  __attribute__((noinline))
+#define ALWAYS_INLINE __attribute__((always_inline))
+#define UNUSED __attribute__((unused))
+
+#define likely(x)   __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+#define COMPILER_MEMORY_FENCE asm volatile("" ::: "memory")
+
+#ifdef NDEBUG
+  #define ALWAYS_ASSERT(expr) (likely((expr)) ? (void)0 : abort())
+#else
+  #define ALWAYS_ASSERT(expr) assert((expr))
+#endif /* NDEBUG */
+
+#define ARRAY_NELEMS(a) (sizeof(a)/sizeof((a)[0]))
+
+#define VERBOSE(expr) ((void)0)
+//#define VERBOSE(expr) (expr)
+
+#ifdef CHECK_INVARIANTS
+  #define INVARIANT(expr) ALWAYS_ASSERT(expr)
+#else
+  #define INVARIANT(expr) ((void)0)
+#endif /* CHECK_INVARIANTS */
+
+// XXX: would be nice if we checked these during single threaded execution
+#define SINGLE_THREADED_INVARIANT(expr) ((void)0)
+
+// tune away
+#define SMALL_SIZE_VEC       128
+#define SMALL_SIZE_MAP       64
+#define EXTRA_SMALL_SIZE_MAP 8
+
+//#define BACKOFF_SPINS_FACTOR 1000
+//#define BACKOFF_SPINS_FACTOR 100
+#define BACKOFF_SPINS_FACTOR 10
+
+// throw exception after the assert(), so that GCC knows
+// we'll never return
+#define NDB_UNIMPLEMENTED(what) \
+  do { \
+    ALWAYS_ASSERT(false); \
+    throw ::std::runtime_error(what); \
+  } while (0)
+
+#ifdef USE_BUILTIN_MEMFUNCS
+#define NDB_MEMCPY __builtin_memcpy
+#define NDB_MEMSET __builtin_memset
+#else
+#define NDB_MEMCPY memcpy
+#define NDB_MEMSET memset
+#endif
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
+#define GCC_AT_LEAST_47 1
+#else
+#define GCC_AT_LEAST_47 0
+#endif
+
+// g++-4.6 does not support override
+#if GCC_AT_LEAST_47
+#define OVERRIDE override
+#else
+#define OVERRIDE
+#endif
+
+// number of nanoseconds in 1 second (1e9)
+#define ONE_SECOND_NS 1000000000
+
+#endif /* _MACROS_H_ */
diff --git a/silo/marked_ptr.h b/silo/marked_ptr.h
new file mode 100644 (file)
index 0000000..7fc776d
--- /dev/null
@@ -0,0 +1,170 @@
+#ifndef _MARKED_PTR_H_
+#define _MARKED_PTR_H_
+
+#include <functional>
+#include <iostream>
+
+#include "macros.h"
+#include "util.h"
+
+template <typename T>
+class marked_ptr {
+public:
+
+  // can take the bottom 3 bits of a ptr [ptrs must be 8-byte aligned]
+  static const uintptr_t LowBitsMask = 0x7;
+
+  constexpr inline marked_ptr() : px(0) {}
+
+  template <typename U>
+  inline marked_ptr(U *px) : px(reinterpret_cast<uintptr_t>(px))
+  {
+    // type-safety
+    if (static_cast<T *>(px))
+      ;
+    INVARIANT(!(this->px & LowBitsMask));
+  }
+
+  // both operator= and copy-ctor PROPAGATE mark bits
+
+  template <typename U>
+  inline marked_ptr(const marked_ptr<U> &o)
+    : px(o.px)
+  {
+    // type-safety
+    if (static_cast<T *>((U *) nullptr))
+      ;
+  }
+
+  template <typename U>
+  inline marked_ptr &
+  operator=(const marked_ptr<U> &o)
+  {
+    // type-safety
+    if (static_cast<T *>((U *) nullptr))
+      ;
+    px = o.px;
+    return *this;
+  }
+
+  inline
+  operator bool() const
+  {
+    return get();
+  }
+
+  inline T *
+  operator->() const
+  {
+    return get();
+  }
+
+  inline T &
+  operator*() const
+  {
+    return *get();
+  }
+
+  inline T *
+  get() const
+  {
+    return reinterpret_cast<T *>(px & ~LowBitsMask);
+  }
+
+  template <typename U>
+  inline void
+  reset(U *px)
+  {
+    INVARIANT(!(px & LowBitsMask));
+    // type-safety
+    if (static_cast<T *>(px))
+      ;
+    this->px = reinterpret_cast<uintptr_t>(px);
+  }
+
+  inline uint8_t
+  get_flags() const
+  {
+    return px & LowBitsMask;
+  }
+
+  inline void
+  set_flags(uint8_t flags)
+  {
+    INVARIANT(!(flags & ~LowBitsMask));
+    px = (px & ~LowBitsMask) | flags;
+  }
+
+  inline void
+  or_flags(uint8_t flags)
+  {
+    INVARIANT(!(flags & ~LowBitsMask));
+    px |= flags;
+  }
+
+  template <typename U>
+  inline bool
+  operator==(const marked_ptr<U> &o) const
+  {
+    return get() == o.get();
+  }
+
+  template <typename U>
+  inline bool
+  operator!=(const marked_ptr<U> &o) const
+  {
+    return !operator==(o);
+  }
+
+  template <typename U>
+  inline bool
+  operator<(const marked_ptr<U> &o) const
+  {
+    return get() < o.get();
+  }
+
+  template <typename U>
+  inline bool
+  operator>=(const marked_ptr<U> &o) const
+  {
+    return !operator<(o);
+  }
+
+  template <typename U>
+  inline bool
+  operator>(const marked_ptr<U> &o) const
+  {
+    return get() > o.get();
+  }
+
+  template <typename U>
+  inline bool
+  operator<=(const marked_ptr<U> &o) const
+  {
+    return !operator>(o);
+  }
+
+private:
+  uintptr_t px;
+};
+
+template <typename T>
+inline std::ostream &
+operator<<(std::ostream &o, const marked_ptr<T> &p)
+{
+  o << "[px=" << util::hexify(p.get()) << ", flags=0x" << util::hexify(p.get_flags()) << "]";
+  return o;
+}
+
+namespace std {
+  template <typename T>
+  struct hash<marked_ptr<T>> {
+    inline size_t
+    operator()(marked_ptr<T> p) const
+    {
+      return hash<T *>()(p.get());
+    }
+  };
+}
+
+#endif /* _MARKED_PTR_H_ */
diff --git a/silo/masstree/AUTHORS b/silo/masstree/AUTHORS
new file mode 100644 (file)
index 0000000..8cf787f
--- /dev/null
@@ -0,0 +1,8 @@
+Eddie Kohler
+kohler@seas.harvard.edu
+
+Yandong Mao
+ydmao@csail.mit.edu
+
+Robert Morris
+rtm@csail.mit.edu
diff --git a/silo/masstree/GNUmakefile b/silo/masstree/GNUmakefile
new file mode 100644 (file)
index 0000000..d6cd8b6
--- /dev/null
@@ -0,0 +1,95 @@
+AR = ar
+CC = gcc
+CXX = g++ -std=gnu++0x
+CFLAGS = -g -W -Wall -O3
+DEPSDIR := .deps
+DEPCFLAGS = -MD -MF $(DEPSDIR)/$*.d -MP
+ifeq ($(strip $(MEMMGR)), )
+  MEMMGR = -ljemalloc
+endif
+ifneq ($(strip $(KEYSWAP)), )
+  CFLAGS += -DKEYSWAP
+endif
+ifneq ($(strip $(NOPREFETCH)), )
+  CFLAGS += -DNOPREFETCH
+endif
+ifneq ($(strip $(NOSUPERPAGE)), )
+  CFLAGS += -DNOSUPERPAGE
+endif
+LIBS = -lnuma  -lpthread -lm
+LDFLAGS = 
+
+all: test_atomics mtd mtclient mttest
+
+%.o: %.c config.h $(DEPSDIR)/stamp
+       $(CXX) $(CFLAGS) $(DEPCFLAGS) -include config.h -c -o $@ $<
+
+%.o: %.cc config.h $(DEPSDIR)/stamp
+       $(CXX) $(CFLAGS) $(DEPCFLAGS) -include config.h -c -o $@ $<
+
+%.S: %.o
+       objdump -S $< > $@
+
+libjson.a: json.o string.o straccum.o str.o msgpack.o \
+       clp.o kvrandom.o compiler.o kvthread.o
+       @/bin/rm -f $@
+       $(AR) cru $@ $^
+
+KVTREES = query_masstree.o \
+       value_string.o value_array.o value_versioned_array.o \
+       string_slice.o
+
+mtd: mtd.o log.o checkpoint.o file.o misc.o $(KVTREES) \
+       kvio.o libjson.a
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+mtclient: mtclient.o misc.o testrunner.o kvio.o libjson.a
+       $(CXX) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
+
+mttest: mttest.o misc.o checkpoint.o $(KVTREES) testrunner.o \
+       kvio.o libjson.a
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+test_string: test_string.o string.o straccum.o compiler.o
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+test_atomics: test_atomics.o string.o straccum.o kvrandom.o \
+       json.o compiler.o kvio.o
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+jsontest: jsontest.o string.o straccum.o json.o compiler.o
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+msgpacktest: msgpacktest.o string.o straccum.o json.o compiler.o msgpack.o
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+config.h: stamp-h
+
+GNUmakefile: GNUmakefile.in config.status
+       CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+configure config.h.in: configure.ac
+       autoreconf -i
+       touch config.h.in
+
+config.status: configure
+       ./configure  '--enable-max-key-len=1024' '--disable-assertions' '--enable-invariants' '--enable-preconditions' '--with-malloc=jemalloc'
+
+$(DEPSDIR)/stamp:
+       mkdir -p $(DEPSDIR)
+       touch $@
+
+stamp-h: config.h.in config.status
+       CONFIG_FILES= $(SHELL) ./config.status
+       echo > stamp-h
+
+clean:
+       rm -f mtd mtclient mttest test_string test_atomics *.o libjson.a
+       rm -rf .deps
+
+DEPFILES := $(wildcard $(DEPSDIR)/*.d)
+ifneq ($(DEPFILES),)
+include $(DEPFILES)
+endif
+
+.PHONY: clean all
diff --git a/silo/masstree/GNUmakefile.in b/silo/masstree/GNUmakefile.in
new file mode 100644 (file)
index 0000000..e1d37db
--- /dev/null
@@ -0,0 +1,95 @@
+AR = ar
+CC = @CC@
+CXX = @CXX@
+CFLAGS = -g -W -Wall -O3
+DEPSDIR := .deps
+DEPCFLAGS = -MD -MF $(DEPSDIR)/$*.d -MP
+ifeq ($(strip $(MEMMGR)), )
+  MEMMGR = @MALLOC_LIBS@
+endif
+ifneq ($(strip $(KEYSWAP)), )
+  CFLAGS += -DKEYSWAP
+endif
+ifneq ($(strip $(NOPREFETCH)), )
+  CFLAGS += -DNOPREFETCH
+endif
+ifneq ($(strip $(NOSUPERPAGE)), )
+  CFLAGS += -DNOSUPERPAGE
+endif
+LIBS = @LIBS@ -lpthread -lm
+LDFLAGS = @LDFLAGS@
+
+all: test_atomics mtd mtclient mttest
+
+%.o: %.c config.h $(DEPSDIR)/stamp
+       $(CXX) $(CFLAGS) $(DEPCFLAGS) -include config.h -c -o $@ $<
+
+%.o: %.cc config.h $(DEPSDIR)/stamp
+       $(CXX) $(CFLAGS) $(DEPCFLAGS) -include config.h -c -o $@ $<
+
+%.S: %.o
+       objdump -S $< > $@
+
+libjson.a: json.o string.o straccum.o str.o msgpack.o \
+       clp.o kvrandom.o compiler.o kvthread.o
+       @/bin/rm -f $@
+       $(AR) cru $@ $^
+
+KVTREES = query_masstree.o \
+       value_string.o value_array.o value_versioned_array.o \
+       string_slice.o
+
+mtd: mtd.o log.o checkpoint.o file.o misc.o $(KVTREES) \
+       kvio.o libjson.a
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+mtclient: mtclient.o misc.o testrunner.o kvio.o libjson.a
+       $(CXX) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
+
+mttest: mttest.o misc.o checkpoint.o $(KVTREES) testrunner.o \
+       kvio.o libjson.a
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+test_string: test_string.o string.o straccum.o compiler.o
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+test_atomics: test_atomics.o string.o straccum.o kvrandom.o \
+       json.o compiler.o kvio.o
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+jsontest: jsontest.o string.o straccum.o json.o compiler.o
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+msgpacktest: msgpacktest.o string.o straccum.o json.o compiler.o msgpack.o
+       $(CXX) $(CFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
+
+config.h: stamp-h
+
+GNUmakefile: GNUmakefile.in config.status
+       CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+configure config.h.in: configure.ac
+       autoreconf -i
+       touch config.h.in
+
+config.status: configure
+       ./configure @ac_configure_args@
+
+$(DEPSDIR)/stamp:
+       mkdir -p $(DEPSDIR)
+       touch $@
+
+stamp-h: config.h.in config.status
+       CONFIG_FILES= $(SHELL) ./config.status
+       echo > stamp-h
+
+clean:
+       rm -f mtd mtclient mttest test_string test_atomics *.o libjson.a
+       rm -rf .deps
+
+DEPFILES := $(wildcard $(DEPSDIR)/*.d)
+ifneq ($(DEPFILES),)
+include $(DEPFILES)
+endif
+
+.PHONY: clean all
diff --git a/silo/masstree/LICENSE b/silo/masstree/LICENSE
new file mode 100644 (file)
index 0000000..d83b1be
--- /dev/null
@@ -0,0 +1,34 @@
+This software is subject to the license below, which is referenced
+elsewhere using the phrase "the Masstree LICENSE file". This license
+is an MIT license, plus a clause (taken from the W3C license)
+requiring prior written permission to use our names in publicity. The
+AUTHORS file lists the people who have contributed to this software.
+
+===========================================================================
+
+(c) 2011-2013 President and Fellows of Harvard College
+(c) 2010-2011 Regents of the University of California
+(c) 2010-2013 Massachusetts Institute of Technology
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+The name and trademarks of copyright holders may NOT be used in advertising
+or publicity pertaining to the Software without specific, written prior
+permission. Title to copyright in this Software and any associated
+documentation will at all times remain with copyright holders.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/silo/masstree/README.md b/silo/masstree/README.md
new file mode 100644 (file)
index 0000000..9e16462
--- /dev/null
@@ -0,0 +1,160 @@
+# Masstree #
+
+This is the source release for Masstree, a fast, multi-core key-value
+store. This document describes how to run Masstree and interpret its
+results.
+
+## Contents ##
+
+* `MTDIR`                     This directory
+* `MTDIR/doc`               Masstree algorithm specification
+
+## Installation ##
+
+Masstree is tested on Debian, Ubuntu and Mac OS X. To build:
+
+    $ ./configure
+    $ make
+
+For performance measurements, you should disable assertions.
+
+    $ ./configure --disable-assertions
+
+Masstree needs a fast malloc, and can link with jemalloc, Google’s
+tcmalloc, Hoard, or our own Flow allocator. It will normally choose
+jemalloc or tcmalloc, if it finds them. To use a specific memory
+allocator:
+
+    ./configure --with-malloc=<jemalloc|tcmalloc|flow|hoard>
+
+Flow is our re-implementation of
+[Streamflow](http://people.cs.vt.edu/~scschnei/streamflow/) allocator,
+and may be open-sourced in future.
+
+See `./configure --help` for more configure options.
+
+## Testing ##
+
+The simplest way to try out Masstree is the `./mttest` program.
+This test doesn’t involve disk or network overhead.
+
+<pre>
+$ ./mttest
+1/1 rw1/m
+0: now getting
+1: now getting
+0: {"table":"mb","test":"rw1","trial":0,"thread":0,"puts":13243551,"puts_per_sec":1324492.05531,"gets":13243551,"gets_per_sec":1497267.13928,"ops":26487102,"ops_per_sec":1405590.1258}
+1: {"table":"mb","test":"rw1","trial":0,"thread":1,"puts":13242601,"puts_per_sec":1324397.45602,"gets":13242601,"gets_per_sec":1481151.35726,"ops":26485202,"ops_per_sec":1398395.26601}
+EXPERIMENT x0
+</pre>
+
+The test starts a process which hosts a Masstree, and generates and
+executes queries over the tree. It uses all available cores (two in
+the above example). The test lasts for 20 seconds. It populates the
+key-value store with `put` queries during first 10 seconds, and then
+issues `get` queries over the tree during the next 10 seconds. See
+`kvtest_rw1_seed` in `kvtest.hh` for more details about the workload.
+For a list of workloads, run `./mttest --help`.
+
+The output summarizes the throughput of each core. The `1/1 rw1/m` line says
+that `mttest` is running the first trial (out of one trials), of the `rw1`
+workload using Masstree (`m` for short) as the internal data structure.
+When the run completes (the `now getting` lines are printed during the
+test), `mttest` generates a per-core throughput summary, as indicated by
+`0: {"table":"mb","test":"rw1",...}`.
+
+If you redirect its standard output to a file or pipe, `mttest` will produce
+gnuplot source that plots the median per-core throughput. Each candlestick
+has five points for the min,20%,50%,70%,max of the corresponding metrics
+among all threads.
+
+`mttest` also writes the output as JSON into file for further analysis. For
+example, after `./mttest`, `notebook-mttest.json` will contain:
+
+<pre>
+{
+  "experiments":{
+    "x0":{
+      "git-revision":"673994c43d58d46f4ebf3f7d4e1fce19074594cb",
+      "time":"Wed Oct 24 14:54:39 2012",
+      "machine":"mat",
+      "cores":2,
+      "runs":["x0\/rw1\/mb\/0"]
+    }
+  },
+  "data":{
+    "x0\/rw1\/mb\/0":[
+      {
+        "table":"mb",
+        "test":"rw1",
+        "trial":0,
+        "thread":0,
+        "puts":13243551,
+        "puts_per_sec":1324492.05531,
+        "gets":13243551,
+        "gets_per_sec":1497267.13928,
+        "ops":26487102,
+        "ops_per_sec":1405590.1258
+      },
+      {
+        "table":"mb",
+        "test":"rw1",
+        "trial":0,
+        "thread":1,
+        "puts":13242601,
+        "puts_per_sec":1324397.45602,
+        "gets":13242601,
+        "gets_per_sec":1481151.35726,
+        "ops":26485202,
+        "ops_per_sec":1398395.26601
+      }
+    ]
+  }
+}
+</pre>
+
+Run `./mttest --help` for a list of tests and options.
+
+## Network testing ##
+
+`mtclient` supports almost the same set of workloads that `mttest` does, but it
+sends queries to a Masstree server over the network.
+
+To start the Masstree server, run:
+
+<pre>
+$ ./mtd --logdir=[LOG_DIRS] --ckdir=[CHECKPOINT_DIRS]
+mb, Bag, pin-threads disabled, logging enabled
+no ./kvd-ckp-gen
+no ./kvd-ckp-0-0
+no ./kvd-ckp-0-1
+2 udp threads
+2 tcp threads
+</pre>
+
+`LOG_DIRS` is a comma-separated list of directories storing Masstree
+logs, and `CHECKPOINT_DIRS` is a comma-separated list of directories
+storing Masstree checkpoints. Masstree will write its logs to the
+`LOG_DIRS` and periodic checkpoints to the `CHECKPOINT_DIRS`. (Both
+logging and multithreading are performed using multiple cores, so
+there are several log and checkpoint files.) Alternatively, run `./mtd
+-n` to turn off logging.
+
+To run the `rw1` workload with `mtclient` on the same machine as
+`mtd`, run:
+
+<pre>
+$ ./mtclient -s 127.0.0.1 rw1
+tcp, w 500, test rw1, children 2
+0 now getting
+1 now getting
+0 total 7632001 763284 put/s 1263548 get/s
+1 total 7612501 761423 put/s 1259847 get/s
+{"puts":7632001,"puts_per_sec":763284.211682,"gets":7632001,"gets_per_sec":1263548.30195,"ops":15264002,"ops_per_sec":951678.506329}
+{"puts":7612501,"puts_per_sec":761423.014367,"gets":7612501,"gets_per_sec":1259847.22076,"ops":15225002,"ops_per_sec":949182.006246}
+total 30489004
+puts: n 2, total 15244502, average 7622251, min 7612501, max 7632001, stddev 13789
+gets: n 2, total 15244502, average 7622251, min 7612501, max 7632001, stddev 13789
+puts/s: n 2, total 1524707, average 762354, min 761423, max 763284, stddev 1316
+gets/s: n 2, total 2523396, average 1261698, min 1259847, max 1263548, stddev 2617
+</pre>
diff --git a/silo/masstree/_masstree_config.d b/silo/masstree/_masstree_config.d
new file mode 100644 (file)
index 0000000..49ea7f1
--- /dev/null
@@ -0,0 +1 @@
+DEP_MASSTREE_CONFIG:=--enable-max-key-len=1024 --disable-assertions --enable-invariants --enable-preconditions --with-malloc=jemalloc
diff --git a/silo/masstree/autom4te.cache/output.0 b/silo/masstree/autom4te.cache/output.0
new file mode 100644 (file)
index 0000000..c764a23
--- /dev/null
@@ -0,0 +1,7594 @@
+@%:@! /bin/sh
+@%:@ Guess values for system-dependent variables and create Makefiles.
+@%:@ Generated by GNU Autoconf 2.69 for masstree-beta 0.1.
+@%:@ 
+@%:@ 
+@%:@ Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+@%:@ 
+@%:@ 
+@%:@ This configure script is free software; the Free Software Foundation
+@%:@ gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in @%:@(
+  *posix*) :
+    set -o posix ;; @%:@(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in @%:@(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in @%:@ ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in @%:@(
+  *posix*) :
+    set -o posix ;; @%:@(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+  
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+  
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in @%:@(
+        /*)
+          for as_base in sh bash ksh sh5; do
+            # Try only shells that exist, to save several forks.
+            as_shell=$as_dir/$as_base
+            if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+                   { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+                  if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+          done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+             { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in @%:@ ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+
+@%:@ as_fn_executable_p FILE
+@%:@ -----------------------
+@%:@ Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} @%:@ as_fn_executable_p
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} @%:@ as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIB@&t@OBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='masstree-beta'
+PACKAGE_TARNAME='masstree-beta'
+PACKAGE_VERSION='0.1'
+PACKAGE_STRING='masstree-beta 0.1'
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIB@&t@OBJS
+MALLOC_LIBS
+EGREP
+GREP
+CXXCPP
+ac_ct_CXX
+CXXFLAGS
+CXX
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+ac_configure_args
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_malloc
+enable_row_type
+enable_max_key_len
+enable_superpage
+enable_memdebug
+enable_assert
+enable_assertions
+enable_preconditions
+enable_invariants
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CXX
+CXXFLAGS
+CCC
+CXXCPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir runstatedir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures masstree-beta 0.1 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          @<:@@S|@ac_default_prefix@:>@
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          @<:@PREFIX@:>@
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root @<:@DATAROOTDIR/doc/masstree-beta@:>@
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of masstree-beta 0.1:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-row-type=ARG   row type: bag array array_ver str, default bag
+  --enable-max-key-len=ARG 
+                          maximum length of a key in bytes, default 255
+  --disable-superpage     disable superpage support
+  --enable-memdebug       enable memory debugging
+
+  --disable-assertions    disable debugging assertions
+  --disable-preconditions disable precondition assertions
+  --disable-invariants    disable invariant assertions
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-malloc=TYPE      memory allocator
+                          (malloc|jemalloc|tcmalloc|hoard|flow)
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  CXXCPP      C++ preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+masstree-beta configure 0.1
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+@%:@ ac_fn_c_try_compile LINENO
+@%:@ --------------------------
+@%:@ Try to compile conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_compile
+
+@%:@ ac_fn_cxx_try_compile LINENO
+@%:@ ----------------------------
+@%:@ Try to compile conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_try_compile
+
+@%:@ ac_fn_cxx_try_run LINENO
+@%:@ ------------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded. Assumes
+@%:@ that executables *can* be run.
+ac_fn_cxx_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_try_run
+
+@%:@ ac_fn_cxx_try_cpp LINENO
+@%:@ ------------------------
+@%:@ Try to preprocess conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_try_cpp
+
+@%:@ ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES
+@%:@ ---------------------------------------------------------
+@%:@ Tests whether HEADER exists and can be compiled using the include files in
+@%:@ INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_cxx_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_header_compile
+
+@%:@ ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES
+@%:@ ---------------------------------------------------------
+@%:@ Tests whether HEADER exists, giving a warning if it cannot be compiled using
+@%:@ the include files in INCLUDES and setting the cache variable VAR
+@%:@ accordingly.
+ac_fn_cxx_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@include <$2>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_header_mongrel
+
+@%:@ ac_fn_cxx_try_link LINENO
+@%:@ -------------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_try_link
+
+@%:@ ac_fn_cxx_check_type LINENO TYPE VAR INCLUDES
+@%:@ ---------------------------------------------
+@%:@ Tests whether TYPE exists after having included INCLUDES, setting cache
+@%:@ variable VAR accordingly.
+ac_fn_cxx_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+        return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+           return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_type
+
+@%:@ ac_fn_cxx_compute_int LINENO EXPR VAR INCLUDES
+@%:@ ----------------------------------------------
+@%:@ Tries to find the compile-time value of EXPR in a program that includes
+@%:@ INCLUDES, setting VAR accordingly. Returns whether the value could be
+@%:@ computed
+ac_fn_cxx_compute_int ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if test "$cross_compiling" = yes; then
+    # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= 0)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid; break
+else
+  as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+                       if test $ac_lo -le $ac_mid; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) < 0)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_lo=$ac_mid; break
+else
+  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+                       if test $ac_mid -le $ac_hi; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid
+else
+  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in @%:@((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+@%:@include <stdio.h>
+@%:@include <stdlib.h>
+int
+main ()
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    return 1;
+  if (($2) < 0)
+    {
+      long int i = longval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%ld", i);
+    }
+  else
+    {
+      unsigned long int i = ulongval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%lu", i);
+    }
+  /* Do not output a trailing newline, as this causes \r\n confusion
+     on some platforms.  */
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+  echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+  ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+  fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_compute_int
+
+@%:@ ac_fn_cxx_check_decl LINENO SYMBOL VAR INCLUDES
+@%:@ -----------------------------------------------
+@%:@ Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
+@%:@ accordingly.
+ac_fn_cxx_check_decl ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  as_decl_name=`echo $2|sed 's/ *(.*//'`
+  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+@%:@ifndef $as_decl_name
+@%:@ifdef __cplusplus
+  (void) $as_decl_use;
+@%:@else
+  (void) $as_decl_name;
+@%:@endif
+@%:@endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_decl
+
+@%:@ ac_fn_cxx_check_func LINENO FUNC VAR
+@%:@ ------------------------------------
+@%:@ Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_cxx_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_func
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by masstree-beta $as_me 0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in @%:@((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+ac_config_files="$ac_config_files GNUmakefile"
+
+
+
+ac_user_cxx=${CXX+y}
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $@%:@ != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+  
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+  if test -n "$CCC"; then
+    CXX=$CCC
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CXX" && break
+done
+
+  if test "x$ac_ct_CXX" = x; then
+    CXX="g++"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CXX=$ac_ct_CXX
+  fi
+fi
+
+  fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+   ac_cxx_werror_flag=yes
+   ac_cv_prog_cxx_g=no
+   CXXFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+else
+  CXXFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  
+else
+  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+        CXXFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+
+$as_echo "@%:@define WORDS_BIGENDIAN_SET 1" >>confdefs.h
+
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5
+$as_echo_n "checking how to run the C++ preprocessor... " >&6; }
+if test -z "$CXXCPP"; then
+  if ${ac_cv_prog_CXXCPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CXXCPP needs to be expanded
+    for CXXCPP in "$CXX -E" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+                    Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CXXCPP=$CXXCPP
+  
+fi
+  CXXCPP=$ac_cv_prog_CXXCPP
+else
+  ac_cv_prog_CXXCPP=$CXXCPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5
+$as_echo "$CXXCPP" >&6; }
+ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+                    Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+  
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+  
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+  
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+  
+$as_echo "@%:@define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+fi
+
+done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
+if ${ac_cv_c_bigendian+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_bigendian=unknown
+    # See if we're dealing with a universal compiler.
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifndef __APPLE_CC__
+              not a universal capable compiler
+            #endif
+            typedef int dummy;
+           
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  
+       # Check for potential -arch flags.  It is not universal unless
+       # there are at least two -arch flags with different values.
+       ac_arch=
+       ac_prev=
+       for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
+        if test -n "$ac_prev"; then
+          case $ac_word in
+            i?86 | x86_64 | ppc | ppc64)
+              if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
+                ac_arch=$ac_word
+              else
+                ac_cv_c_bigendian=universal
+                break
+              fi
+              ;;
+          esac
+          ac_prev=
+        elif test "x$ac_word" = "x-arch"; then
+          ac_prev=arch
+        fi
+       done
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if sys/param.h defines the BYTE_ORDER macro.
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+            #include <sys/param.h>
+          
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+                    && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+                    && LITTLE_ENDIAN)
+             bogus endian macros
+            #endif
+          
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+               #include <sys/param.h>
+             
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+                not big endian
+               #endif
+             
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+          
+int
+main ()
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+             bogus endian macros
+            #endif
+          
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to _BIG_ENDIAN or not.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+             
+int
+main ()
+{
+#ifndef _BIG_ENDIAN
+                not big endian
+               #endif
+             
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # Compile a test program.
+      if test "$cross_compiling" = yes; then :
+  # Try to guess by grepping values from an object file.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+short int ascii_mm[] =
+                 { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+               short int ascii_ii[] =
+                 { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+               int use_ascii (int i) {
+                 return ascii_mm[i] + ascii_ii[i];
+               }
+               short int ebcdic_ii[] =
+                 { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+               short int ebcdic_mm[] =
+                 { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+               int use_ebcdic (int i) {
+                 return ebcdic_mm[i] + ebcdic_ii[i];
+               }
+               extern int foo;
+             
+int
+main ()
+{
+return use_ascii (foo) == use_ebcdic (foo);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+             ac_cv_c_bigendian=yes
+           fi
+           if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+             if test "$ac_cv_c_bigendian" = unknown; then
+               ac_cv_c_bigendian=no
+             else
+               # finding both strings is unlikely to happen, but who knows?
+               ac_cv_c_bigendian=unknown
+             fi
+           fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+
+            /* Are we little or big endian?  From Harbison&Steele.  */
+            union
+            {
+              long int l;
+              char c[sizeof (long int)];
+            } u;
+            u.l = 1;
+            return u.c[sizeof (long int) - 1] == 1;
+          
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+  ac_cv_c_bigendian=no
+else
+  ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+$as_echo "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+   yes)
+     $as_echo "@%:@define WORDS_BIGENDIAN 1" >>confdefs.h
+;; #(
+   no)
+      ;; #(
+   universal)
+       
+$as_echo "@%:@define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+
+     ;; #(
+   *)
+     as_fn_error $? "unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
+ esac
+
+
+for ac_header in sys/epoll.h numa.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+fi
+
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing numa_available" >&5
+$as_echo_n "checking for library containing numa_available... " >&6; }
+if ${ac_cv_search_numa_available+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char numa_available ();
+int
+main ()
+{
+return numa_available ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' numa; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_search_numa_available=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_numa_available+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_numa_available+:} false; then :
+  
+else
+  ac_cv_search_numa_available=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_numa_available" >&5
+$as_echo "$ac_cv_search_numa_available" >&6; }
+ac_res=$ac_cv_search_numa_available
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  
+$as_echo "@%:@define HAVE_LIBNUMA 1" >>confdefs.h
+
+fi
+
+
+
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clz builtin" >&5
+$as_echo_n "checking for __builtin_clz builtin... " >&6; }
+if ${ac_cv_have___builtin_clz+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned f(unsigned x) { return __builtin_clz(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_clz=yes
+else
+  ac_cv_have___builtin_clz=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_clz" >&5
+$as_echo "$ac_cv_have___builtin_clz" >&6; }
+    if test $ac_cv_have___builtin_clz = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CLZ 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clzl builtin" >&5
+$as_echo_n "checking for __builtin_clzl builtin... " >&6; }
+if ${ac_cv_have___builtin_clzl+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long f(unsigned long x) { return __builtin_clzl(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_clzl=yes
+else
+  ac_cv_have___builtin_clzl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_clzl" >&5
+$as_echo "$ac_cv_have___builtin_clzl" >&6; }
+    if test $ac_cv_have___builtin_clzl = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CLZL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clzll builtin" >&5
+$as_echo_n "checking for __builtin_clzll builtin... " >&6; }
+if ${ac_cv_have___builtin_clzll+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long long f(unsigned long long x) { return __builtin_clzll(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_clzll=yes
+else
+  ac_cv_have___builtin_clzll=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_clzll" >&5
+$as_echo "$ac_cv_have___builtin_clzll" >&6; }
+    if test $ac_cv_have___builtin_clzll = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CLZLL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_ctz builtin" >&5
+$as_echo_n "checking for __builtin_ctz builtin... " >&6; }
+if ${ac_cv_have___builtin_ctz+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned f(unsigned x) { return __builtin_ctz(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_ctz=yes
+else
+  ac_cv_have___builtin_ctz=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_ctz" >&5
+$as_echo "$ac_cv_have___builtin_ctz" >&6; }
+    if test $ac_cv_have___builtin_ctz = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CTZ 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_ctzl builtin" >&5
+$as_echo_n "checking for __builtin_ctzl builtin... " >&6; }
+if ${ac_cv_have___builtin_ctzl+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long f(unsigned long x) { return __builtin_ctzl(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_ctzl=yes
+else
+  ac_cv_have___builtin_ctzl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_ctzl" >&5
+$as_echo "$ac_cv_have___builtin_ctzl" >&6; }
+    if test $ac_cv_have___builtin_ctzl = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CTZL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_ctzll builtin" >&5
+$as_echo_n "checking for __builtin_ctzll builtin... " >&6; }
+if ${ac_cv_have___builtin_ctzll+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long long f(unsigned long long x) { return __builtin_ctzll(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_ctzll=yes
+else
+  ac_cv_have___builtin_ctzll=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_ctzll" >&5
+$as_echo "$ac_cv_have___builtin_ctzll" >&6; }
+    if test $ac_cv_have___builtin_ctzll = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CTZLL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_synchronize builtin" >&5
+$as_echo_n "checking for __sync_synchronize builtin... " >&6; }
+if ${ac_cv_have___sync_synchronize+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long x = 11;
+    void f(long i) { long* y = &x; __sync_synchronize(); *y = i; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_synchronize=yes
+else
+  ac_cv_have___sync_synchronize=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_synchronize" >&5
+$as_echo "$ac_cv_have___sync_synchronize" >&6; }
+    if test $ac_cv_have___sync_synchronize = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_SYNCHRONIZE 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_add builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_add builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_add+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_fetch_and_add(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_add=yes
+else
+  ac_cv_have___sync_fetch_and_add=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_add" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_add" >&6; }
+    if test $ac_cv_have___sync_fetch_and_add = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_FETCH_AND_ADD 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_add_and_fetch builtin" >&5
+$as_echo_n "checking for __sync_add_and_fetch builtin... " >&6; }
+if ${ac_cv_have___sync_add_and_fetch+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_add_and_fetch(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_add_and_fetch=yes
+else
+  ac_cv_have___sync_add_and_fetch=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_add_and_fetch" >&5
+$as_echo "$ac_cv_have___sync_add_and_fetch" >&6; }
+    if test $ac_cv_have___sync_add_and_fetch = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_ADD_AND_FETCH 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_add_8 builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_add_8 builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_add_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_add(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_add_8=yes
+else
+  ac_cv_have___sync_fetch_and_add_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_add_8" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_add_8" >&6; }
+    if test $ac_cv_have___sync_fetch_and_add_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_FETCH_AND_ADD_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_add_and_fetch_8 builtin" >&5
+$as_echo_n "checking for __sync_add_and_fetch_8 builtin... " >&6; }
+if ${ac_cv_have___sync_add_and_fetch_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_add_and_fetch(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_add_and_fetch_8=yes
+else
+  ac_cv_have___sync_add_and_fetch_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_add_and_fetch_8" >&5
+$as_echo "$ac_cv_have___sync_add_and_fetch_8" >&6; }
+    if test $ac_cv_have___sync_add_and_fetch_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_ADD_AND_FETCH_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_or builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_or builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_or+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_fetch_and_or(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_or=yes
+else
+  ac_cv_have___sync_fetch_and_or=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_or" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_or" >&6; }
+    if test $ac_cv_have___sync_fetch_and_or = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_FETCH_AND_OR 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_or_and_fetch builtin" >&5
+$as_echo_n "checking for __sync_or_and_fetch builtin... " >&6; }
+if ${ac_cv_have___sync_or_and_fetch+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_or_and_fetch(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_or_and_fetch=yes
+else
+  ac_cv_have___sync_or_and_fetch=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_or_and_fetch" >&5
+$as_echo "$ac_cv_have___sync_or_and_fetch" >&6; }
+    if test $ac_cv_have___sync_or_and_fetch = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_OR_AND_FETCH 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_or_8 builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_or_8 builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_or_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_or(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_or_8=yes
+else
+  ac_cv_have___sync_fetch_and_or_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_or_8" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_or_8" >&6; }
+    if test $ac_cv_have___sync_fetch_and_or_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_FETCH_AND_OR_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_or_and_fetch_8 builtin" >&5
+$as_echo_n "checking for __sync_or_and_fetch_8 builtin... " >&6; }
+if ${ac_cv_have___sync_or_and_fetch_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_or_and_fetch(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_or_and_fetch_8=yes
+else
+  ac_cv_have___sync_or_and_fetch_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_or_and_fetch_8" >&5
+$as_echo "$ac_cv_have___sync_or_and_fetch_8" >&6; }
+    if test $ac_cv_have___sync_or_and_fetch_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_OR_AND_FETCH_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_bool_compare_and_swap builtin" >&5
+$as_echo_n "checking for __sync_bool_compare_and_swap builtin... " >&6; }
+if ${ac_cv_have___sync_bool_compare_and_swap+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+bool f(long* x, long y, long z) { return __sync_bool_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_bool_compare_and_swap=yes
+else
+  ac_cv_have___sync_bool_compare_and_swap=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_bool_compare_and_swap" >&5
+$as_echo "$ac_cv_have___sync_bool_compare_and_swap" >&6; }
+    if test $ac_cv_have___sync_bool_compare_and_swap = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_bool_compare_and_swap_8 builtin" >&5
+$as_echo_n "checking for __sync_bool_compare_and_swap_8 builtin... " >&6; }
+if ${ac_cv_have___sync_bool_compare_and_swap_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    bool f(int64_t* x, int64_t y, int64_t z) { return __sync_bool_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_bool_compare_and_swap_8=yes
+else
+  ac_cv_have___sync_bool_compare_and_swap_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_bool_compare_and_swap_8" >&5
+$as_echo "$ac_cv_have___sync_bool_compare_and_swap_8" >&6; }
+    if test $ac_cv_have___sync_bool_compare_and_swap_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_val_compare_and_swap builtin" >&5
+$as_echo_n "checking for __sync_val_compare_and_swap builtin... " >&6; }
+if ${ac_cv_have___sync_val_compare_and_swap+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x, long y, long z) { return __sync_val_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_val_compare_and_swap=yes
+else
+  ac_cv_have___sync_val_compare_and_swap=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_val_compare_and_swap" >&5
+$as_echo "$ac_cv_have___sync_val_compare_and_swap" >&6; }
+    if test $ac_cv_have___sync_val_compare_and_swap = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_val_compare_and_swap_8 builtin" >&5
+$as_echo_n "checking for __sync_val_compare_and_swap_8 builtin... " >&6; }
+if ${ac_cv_have___sync_val_compare_and_swap_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x, int64_t y, int64_t z) { return __sync_val_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_val_compare_and_swap_8=yes
+else
+  ac_cv_have___sync_val_compare_and_swap_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_val_compare_and_swap_8" >&5
+$as_echo "$ac_cv_have___sync_val_compare_and_swap_8" >&6; }
+    if test $ac_cv_have___sync_val_compare_and_swap_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_lock_test_and_set builtin" >&5
+$as_echo_n "checking for __sync_lock_test_and_set builtin... " >&6; }
+if ${ac_cv_have___sync_lock_test_and_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_lock_test_and_set(x, 1); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_lock_test_and_set=yes
+else
+  ac_cv_have___sync_lock_test_and_set=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_lock_test_and_set" >&5
+$as_echo "$ac_cv_have___sync_lock_test_and_set" >&6; }
+    if test $ac_cv_have___sync_lock_test_and_set = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_LOCK_TEST_AND_SET 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_lock_test_and_set_val builtin" >&5
+$as_echo_n "checking for __sync_lock_test_and_set_val builtin... " >&6; }
+if ${ac_cv_have___sync_lock_test_and_set_val+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x, long y) { return __sync_lock_test_and_set(x, y); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_lock_test_and_set_val=yes
+else
+  ac_cv_have___sync_lock_test_and_set_val=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_lock_test_and_set_val" >&5
+$as_echo "$ac_cv_have___sync_lock_test_and_set_val" >&6; }
+    if test $ac_cv_have___sync_lock_test_and_set_val = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_lock_release_set builtin" >&5
+$as_echo_n "checking for __sync_lock_release_set builtin... " >&6; }
+if ${ac_cv_have___sync_lock_release_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+void f(long* x) { __sync_lock_release(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_lock_release_set=yes
+else
+  ac_cv_have___sync_lock_release_set=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_lock_release_set" >&5
+$as_echo "$ac_cv_have___sync_lock_release_set" >&6; }
+    if test $ac_cv_have___sync_lock_release_set = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_LOCK_RELEASE_SET 1" >>confdefs.h
+
+    fi
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands 'auto'" >&5
+$as_echo_n "checking whether the C++ compiler understands 'auto'... " >&6; }
+if ${ac_cv_cxx_auto+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_auto=yes
+else
+  ac_cv_cxx_auto=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_auto" >&5
+$as_echo "$ac_cv_cxx_auto" >&6; }
+if test "$ac_cv_cxx_auto" != yes -a -z "$ac_user_cxx"; then
+    CXX="${CXX} -std=gnu++0x"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler with -std=gnu++0x understands 'auto'" >&5
+$as_echo_n "checking whether the C++ compiler with -std=gnu++0x understands 'auto'... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_auto=yes
+else
+  ac_cv_cxx_auto=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_auto" >&5
+$as_echo "$ac_cv_cxx_auto" >&6; }
+fi
+
+if test "$ac_cv_cxx_auto" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_AUTO 1" >>confdefs.h
+
+else
+    as_fn_error $? "
+
+The C++ compiler does not appear to understand C++11.
+To fix this problem, try supplying a \"CXX\" argument to ./configure,
+such as \"./configure CXX='c++ -std=gnu++0x'\".
+
+========================================================" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands constexpr" >&5
+$as_echo_n "checking whether the C++ compiler understands constexpr... " >&6; }
+if ${ac_cv_cxx_constexpr+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+constexpr int f(int x) { return x + 1; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_constexpr=yes
+else
+  ac_cv_cxx_constexpr=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_constexpr" >&5
+$as_echo "$ac_cv_cxx_constexpr" >&6; }
+if test "$ac_cv_cxx_constexpr" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_CONSTEXPR 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands static_assert" >&5
+$as_echo_n "checking whether the C++ compiler understands static_assert... " >&6; }
+if ${ac_cv_cxx_static_assert+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+const int f = 2;
+int
+main ()
+{
+static_assert(f == 2, "f should be 2");
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_static_assert=yes
+else
+  ac_cv_cxx_static_assert=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_static_assert" >&5
+$as_echo "$ac_cv_cxx_static_assert" >&6; }
+if test "$ac_cv_cxx_static_assert" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_STATIC_ASSERT 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands rvalue references" >&5
+$as_echo_n "checking whether the C++ compiler understands rvalue references... " >&6; }
+if ${ac_cv_cxx_rvalue_references+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int f(int &) { return 1; } int f(int &&) { return 0; }
+int
+main ()
+{
+return f(int());
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_rvalue_references=yes
+else
+  ac_cv_cxx_rvalue_references=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_rvalue_references" >&5
+$as_echo "$ac_cv_cxx_rvalue_references" >&6; }
+if test "$ac_cv_cxx_rvalue_references" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_RVALUE_REFERENCES 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands template alias" >&5
+$as_echo_n "checking whether the C++ compiler understands template alias... " >&6; }
+if ${ac_cv_cxx_template_alias+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+template <typename T> struct X { typedef T type; }; template <typename T> using Y = X<T>; int f(int x) { return x; }
+int
+main ()
+{
+return f(Y<int>::type());
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_template_alias=yes
+else
+  ac_cv_cxx_template_alias=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_template_alias" >&5
+$as_echo "$ac_cv_cxx_template_alias" >&6; }
+if test "$ac_cv_cxx_template_alias" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_TEMPLATE_ALIAS 1" >>confdefs.h
+
+fi
+
+for ac_header in type_traits
+do :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "type_traits" "ac_cv_header_type_traits" "$ac_includes_default"
+if test "x$ac_cv_header_type_traits" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_TYPE_TRAITS 1
+_ACEOF
+fi
+
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::hash" >&5
+$as_echo_n "checking for std::hash... " >&6; }
+if ${ac_cv_have_std_hash+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <functional>
+#include <stddef.h>
+int
+main ()
+{
+std::hash<int> h; size_t x = h(1); return x == 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_std_hash=yes
+else
+  ac_cv_have_std_hash=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_std_hash" >&5
+$as_echo "$ac_cv_have_std_hash" >&6; }
+if test $ac_cv_have_std_hash = yes; then
+    
+$as_echo "@%:@define HAVE_STD_HASH 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __has_trivial_copy" >&5
+$as_echo_n "checking for __has_trivial_copy... " >&6; }
+if ${ac_cv_have___has_trivial_copy+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+long x = 1; if (__has_trivial_copy(long)) x = 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have___has_trivial_copy=yes
+else
+  ac_cv_have___has_trivial_copy=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___has_trivial_copy" >&5
+$as_echo "$ac_cv_have___has_trivial_copy" >&6; }
+if test $ac_cv_have___has_trivial_copy = yes; then
+    
+$as_echo "@%:@define HAVE___HAS_TRIVIAL_COPY 1" >>confdefs.h
+
+fi
+
+if test "$ac_cv_cxx_rvalue_references" = yes; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::move" >&5
+$as_echo_n "checking for std::move... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <utility>
+int
+main ()
+{
+long x = 0; long &&y = std::move(x);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_std_move=yes
+else
+  ac_cv_std_move=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_std_move" >&5
+$as_echo "$ac_cv_std_move" >&6; }
+    if test "$ac_cv_std_move" != yes; then
+        as_fn_error $? "
+
+The C++ compiler understands C++11, but does not have std::move.
+If you are using clang on Mac, ensure the -stdlib=libc++ option.
+
+========================================================" "$LINENO" 5
+    fi
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::is_trivially_copyable" >&5
+$as_echo_n "checking for std::is_trivially_copyable... " >&6; }
+if ${ac_cv_have_std_is_trivially_copyable+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <type_traits>
+int
+main ()
+{
+return std::is_trivially_copyable<int>::value;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_std_is_trivially_copyable=yes
+else
+  ac_cv_have_std_is_trivially_copyable=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_std_is_trivially_copyable" >&5
+$as_echo "$ac_cv_have_std_is_trivially_copyable" >&6; }
+if test $ac_cv_have_std_is_trivially_copyable = yes; then
+    
+$as_echo "@%:@define HAVE_STD_IS_TRIVIALLY_COPYABLE 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::is_rvalue_reference" >&5
+$as_echo_n "checking for std::is_rvalue_reference... " >&6; }
+if ${ac_cv_have_std_is_rvalue_reference+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <type_traits>
+int
+main ()
+{
+return std::is_rvalue_reference<int>::value;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_std_is_rvalue_reference=yes
+else
+  ac_cv_have_std_is_rvalue_reference=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_std_is_rvalue_reference" >&5
+$as_echo "$ac_cv_have_std_is_rvalue_reference" >&6; }
+if test $ac_cv_have_std_is_rvalue_reference = yes; then
+    
+$as_echo "@%:@define HAVE_STD_IS_RVALUE_REFERENCE 1" >>confdefs.h
+
+fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc in -lflow" >&5
+$as_echo_n "checking for malloc in -lflow... " >&6; }
+if ${ac_cv_lib_flow_malloc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lflow  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char malloc ();
+int
+main ()
+{
+return malloc ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_flow_malloc=yes
+else
+  ac_cv_lib_flow_malloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_flow_malloc" >&5
+$as_echo "$ac_cv_lib_flow_malloc" >&6; }
+if test "x$ac_cv_lib_flow_malloc" = xyes; then :
+  have_flow=true
+else
+  have_flow=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mallctl in -ljemalloc" >&5
+$as_echo_n "checking for mallctl in -ljemalloc... " >&6; }
+if ${ac_cv_lib_jemalloc_mallctl+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ljemalloc  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char mallctl ();
+int
+main ()
+{
+return mallctl ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_jemalloc_mallctl=yes
+else
+  ac_cv_lib_jemalloc_mallctl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jemalloc_mallctl" >&5
+$as_echo "$ac_cv_lib_jemalloc_mallctl" >&6; }
+if test "x$ac_cv_lib_jemalloc_mallctl" = xyes; then :
+  have_jemalloc=true
+else
+  have_jemalloc=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tc_malloc in -ltcmalloc_minimal" >&5
+$as_echo_n "checking for tc_malloc in -ltcmalloc_minimal... " >&6; }
+if ${ac_cv_lib_tcmalloc_minimal_tc_malloc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltcmalloc_minimal  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tc_malloc ();
+int
+main ()
+{
+return tc_malloc ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_tcmalloc_minimal_tc_malloc=yes
+else
+  ac_cv_lib_tcmalloc_minimal_tc_malloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tcmalloc_minimal_tc_malloc" >&5
+$as_echo "$ac_cv_lib_tcmalloc_minimal_tc_malloc" >&6; }
+if test "x$ac_cv_lib_tcmalloc_minimal_tc_malloc" = xyes; then :
+  have_tcmalloc_minimal=true
+else
+  have_tcmalloc_minimal=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Z16getMainHoardHeapv in -lhoard" >&5
+$as_echo_n "checking for _Z16getMainHoardHeapv in -lhoard... " >&6; }
+if ${ac_cv_lib_hoard__Z16getMainHoardHeapv+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lhoard  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char _Z16getMainHoardHeapv ();
+int
+main ()
+{
+return _Z16getMainHoardHeapv ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_hoard__Z16getMainHoardHeapv=yes
+else
+  ac_cv_lib_hoard__Z16getMainHoardHeapv=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_hoard__Z16getMainHoardHeapv" >&5
+$as_echo "$ac_cv_lib_hoard__Z16getMainHoardHeapv" >&6; }
+if test "x$ac_cv_lib_hoard__Z16getMainHoardHeapv" = xyes; then :
+  have_hoard=true
+else
+  have_hoard=
+fi
+
+
+
+@%:@ Check whether --with-malloc was given.
+if test "${with_malloc+set}" = set; then :
+  withval=$with_malloc; ac_mtd_malloc=$withval
+else
+  ac_mtd_malloc=yes
+fi
+
+
+if test \( "$ac_mtd_malloc" = tcmalloc -a -z "$have_tcmalloc_minimal" \) \
+       -o \( "$ac_mtd_malloc" = jemalloc -a -z "$have_jemalloc" \) \
+       -o \( "$ac_mtd_malloc" = flow -a -z "$have_flow" \) \
+        -o \( "$ac_mtd_malloc" = hoard -a -z "$have_hoard" \) ; then
+    as_fn_error $? "$ac_mtd_malloc not found" "$LINENO" 5
+elif test "$ac_mtd_malloc" = tcmalloc -o "$ac_mtd_malloc" = jemalloc -o "$ac_mtd_malloc" = flow -o "$ac_mtd_malloc" = hoard; then
+    :
+elif test "$ac_mtd_malloc" = yes -o "$ac_mtd_malloc" = default; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc library" >&5
+$as_echo_n "checking for malloc library... " >&6; }
+    if test -n "$have_flow"; then ac_mtd_malloc=flow;
+    elif test -n "$have_jemalloc"; then ac_mtd_malloc=jemalloc;
+    elif test -n "$have_tcmalloc_minimal"; then ac_mtd_malloc=tcmalloc;
+    else ac_mtd_malloc=malloc; fi
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_mtd_malloc" >&5
+$as_echo "$ac_mtd_malloc" >&6; }
+elif test "$ac_mtd_malloc" = no -o "$ac_mtd_malloc" = malloc -o -z "$ac_mtd_malloc"; then
+    ac_mtd_malloc=malloc
+else
+    as_fn_error $? "Unknown malloc type $ac_mtd_malloc" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc library" >&5
+$as_echo_n "checking for malloc library... " >&6; }
+if test "$ac_mtd_malloc" = tcmalloc; then
+    MALLOC_LIBS="-ltcmalloc_minimal"
+    
+$as_echo "@%:@define HAVE_TCMALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -ltcmalloc_minimal" >&5
+$as_echo "-ltcmalloc_minimal" >&6; }
+elif test "$ac_mtd_malloc" = jemalloc; then
+    MALLOC_LIBS="-ljemalloc"
+    
+$as_echo "@%:@define HAVE_JEMALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -ljemalloc" >&5
+$as_echo "-ljemalloc" >&6; }
+elif test "$ac_mtd_malloc" = flow; then
+    MALLOC_LIBS="-lflow"
+    
+$as_echo "@%:@define HAVE_FLOW_MALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -lflow" >&5
+$as_echo "-lflow" >&6; }
+elif test "$ac_mtd_malloc" = hoard; then
+    MALLOC_LIBS="-lhoard"
+    
+$as_echo "@%:@define HAVE_HOARD_MALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -lhoard" >&5
+$as_echo "-lhoard" >&6; }
+else
+    MALLOC_LIBS=
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: default" >&5
+$as_echo "default" >&6; }
+fi
+
+
+
+
+
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether off_t and long are the same type" >&5
+$as_echo_n "checking whether off_t and long are the same type... " >&6; }
+if ${ac_cv_have_same_type_off_t_is_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(off_t) {return 0;} int f(long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_off_t_is_long=no
+else
+  ac_cv_have_same_type_off_t_is_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_off_t_is_long" >&5
+$as_echo "$ac_cv_have_same_type_off_t_is_long" >&6; }
+    if test $ac_cv_have_same_type_off_t_is_long = yes; then
+       
+$as_echo "@%:@define HAVE_OFF_T_IS_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether off_t and long long are the same type" >&5
+$as_echo_n "checking whether off_t and long long are the same type... " >&6; }
+if ${ac_cv_have_same_type_off_t_is_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(off_t) {return 0;} int f(long long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_off_t_is_long_long=no
+else
+  ac_cv_have_same_type_off_t_is_long_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_off_t_is_long_long" >&5
+$as_echo "$ac_cv_have_same_type_off_t_is_long_long" >&6; }
+    if test $ac_cv_have_same_type_off_t_is_long_long = yes; then
+       
+$as_echo "@%:@define HAVE_OFF_T_IS_LONG_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether int64_t and long are the same type" >&5
+$as_echo_n "checking whether int64_t and long are the same type... " >&6; }
+if ${ac_cv_have_same_type_int64_t_is_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+int f(int64_t) {return 0;} int f(long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_int64_t_is_long=no
+else
+  ac_cv_have_same_type_int64_t_is_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_int64_t_is_long" >&5
+$as_echo "$ac_cv_have_same_type_int64_t_is_long" >&6; }
+    if test $ac_cv_have_same_type_int64_t_is_long = yes; then
+       
+$as_echo "@%:@define HAVE_INT64_T_IS_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether int64_t and long long are the same type" >&5
+$as_echo_n "checking whether int64_t and long long are the same type... " >&6; }
+if ${ac_cv_have_same_type_int64_t_is_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+int f(int64_t) {return 0;} int f(long long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_int64_t_is_long_long=no
+else
+  ac_cv_have_same_type_int64_t_is_long_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_int64_t_is_long_long" >&5
+$as_echo "$ac_cv_have_same_type_int64_t_is_long_long" >&6; }
+    if test $ac_cv_have_same_type_int64_t_is_long_long = yes; then
+       
+$as_echo "@%:@define HAVE_INT64_T_IS_LONG_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t and unsigned are the same type" >&5
+$as_echo_n "checking whether size_t and unsigned are the same type... " >&6; }
+if ${ac_cv_have_same_type_size_t_is_unsigned+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(size_t) {return 0;} int f(unsigned) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_size_t_is_unsigned=no
+else
+  ac_cv_have_same_type_size_t_is_unsigned=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_size_t_is_unsigned" >&5
+$as_echo "$ac_cv_have_same_type_size_t_is_unsigned" >&6; }
+    if test $ac_cv_have_same_type_size_t_is_unsigned = yes; then
+       
+$as_echo "@%:@define HAVE_SIZE_T_IS_UNSIGNED 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t and unsigned long are the same type" >&5
+$as_echo_n "checking whether size_t and unsigned long are the same type... " >&6; }
+if ${ac_cv_have_same_type_size_t_is_unsigned_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(size_t) {return 0;} int f(unsigned long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_size_t_is_unsigned_long=no
+else
+  ac_cv_have_same_type_size_t_is_unsigned_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_size_t_is_unsigned_long" >&5
+$as_echo "$ac_cv_have_same_type_size_t_is_unsigned_long" >&6; }
+    if test $ac_cv_have_same_type_size_t_is_unsigned_long = yes; then
+       
+$as_echo "@%:@define HAVE_SIZE_T_IS_UNSIGNED_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t and unsigned long long are the same type" >&5
+$as_echo_n "checking whether size_t and unsigned long long are the same type... " >&6; }
+if ${ac_cv_have_same_type_size_t_is_unsigned_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(size_t) {return 0;} int f(unsigned long long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_size_t_is_unsigned_long_long=no
+else
+  ac_cv_have_same_type_size_t_is_unsigned_long_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_size_t_is_unsigned_long_long" >&5
+$as_echo "$ac_cv_have_same_type_size_t_is_unsigned_long_long" >&6; }
+    if test $ac_cv_have_same_type_size_t_is_unsigned_long_long = yes; then
+       
+$as_echo "@%:@define HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+ac_fn_cxx_check_type "$LINENO" "long long" "ac_cv_type_long_long" "$ac_includes_default"
+if test "x$ac_cv_type_long_long" = xyes; then :
+  
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_LONG_LONG 1
+_ACEOF
+
+
+fi
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of short" >&5
+$as_echo_n "checking size of short... " >&6; }
+if ${ac_cv_sizeof_short+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_short" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (short)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_short=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5
+$as_echo "$ac_cv_sizeof_short" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_SHORT $ac_cv_sizeof_short
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5
+$as_echo_n "checking size of int... " >&6; }
+if ${ac_cv_sizeof_int+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_int" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (int)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_int=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5
+$as_echo "$ac_cv_sizeof_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_INT $ac_cv_sizeof_int
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5
+$as_echo_n "checking size of long... " >&6; }
+if ${ac_cv_sizeof_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5
+$as_echo "$ac_cv_sizeof_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_LONG $ac_cv_sizeof_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5
+$as_echo_n "checking size of long long... " >&6; }
+if ${ac_cv_sizeof_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_long_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long long)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_long_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5
+$as_echo "$ac_cv_sizeof_long_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5
+$as_echo_n "checking size of void *... " >&6; }
+if ${ac_cv_sizeof_void_p+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_void_p" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (void *)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_void_p=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5
+$as_echo "$ac_cv_sizeof_void_p" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_VOID_P $ac_cv_sizeof_void_p
+_ACEOF
+
+
+
+ac_fn_cxx_check_decl "$LINENO" "getline" "ac_cv_have_decl_getline" "$ac_includes_default"
+if test "x$ac_cv_have_decl_getline" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_DECL_GETLINE $ac_have_decl
+_ACEOF
+
+
+for ac_header in time.h execinfo.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+fi
+
+done
+
+ac_fn_cxx_check_decl "$LINENO" "clock_gettime" "ac_cv_have_decl_clock_gettime" "#if HAVE_TIME_H
+# include <time.h>
+#endif
+"
+if test "x$ac_cv_have_decl_clock_gettime" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_DECL_CLOCK_GETTIME $ac_have_decl
+_ACEOF
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
+$as_echo_n "checking for library containing clock_gettime... " >&6; }
+if ${ac_cv_search_clock_gettime+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' rt; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_search_clock_gettime=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_clock_gettime+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_clock_gettime+:} false; then :
+  
+else
+  ac_cv_search_clock_gettime=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
+$as_echo "$ac_cv_search_clock_gettime" >&6; }
+ac_res=$ac_cv_search_clock_gettime
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  
+fi
+
+for ac_func in clock_gettime
+do :
+  ac_fn_cxx_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime"
+if test "x$ac_cv_func_clock_gettime" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_CLOCK_GETTIME 1
+_ACEOF
+fi
+done
+
+
+
+@%:@ Check whether --enable-row-type was given.
+if test "${enable_row_type+set}" = set; then :
+  enableval=$enable_row_type; ac_cv_row_type=$enableval
+else
+  ac_cv_row_type=bag
+fi
+
+if test "$ac_cv_row_type" = array; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_ROW_TYPE_ARRAY 1
+_ACEOF
+
+elif test "$ac_cv_row_type" = array_ver; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_ROW_TYPE_ARRAY_VER 1
+_ACEOF
+
+elif test "$ac_cv_row_type" = bag; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_ROW_TYPE_BAG 1
+_ACEOF
+
+elif test "$ac_cv_row_type" = str; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_ROW_TYPE_STR 1
+_ACEOF
+
+else
+    as_fn_error $? "$ac_cv_row_type: Unknown row type" "$LINENO" 5
+fi
+
+@%:@ Check whether --enable-max-key-len was given.
+if test "${enable_max_key_len+set}" = set; then :
+  enableval=$enable_max_key_len; ac_cv_max_key_len=$enableval
+else
+  ac_cv_max_key_len=255
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_MAXKEYLEN $ac_cv_max_key_len
+_ACEOF
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether MADV_HUGEPAGE is supported" >&5
+$as_echo_n "checking whether MADV_HUGEPAGE is supported... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/mman.h>
+#ifndef MADV_HUGEPAGE
+#error "no"
+#endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  have_madv_hugepage=yes
+else
+  have_madv_hugepage=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_madv_hugepage" >&5
+$as_echo "$have_madv_hugepage" >&6; }
+if test $have_madv_hugepage = yes; then
+    
+$as_echo "@%:@define HAVE_MADV_HUGEPAGE 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether MAP_HUGETLB is supported" >&5
+$as_echo_n "checking whether MAP_HUGETLB is supported... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/mman.h>
+#ifndef MAP_HUGETLB
+#error "no"
+#endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  have_map_hugetlb=yes
+else
+  have_map_hugetlb=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_map_hugetlb" >&5
+$as_echo "$have_map_hugetlb" >&6; }
+if test $have_map_hugetlb = yes; then
+    
+$as_echo "@%:@define HAVE_MAP_HUGETLB 1" >>confdefs.h
+
+fi
+
+@%:@ Check whether --enable-superpage was given.
+if test "${enable_superpage+set}" = set; then :
+  enableval=$enable_superpage; 
+else
+  enable_superpage=maybe
+fi
+
+if test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" = "yes no no"; then
+    as_fn_error $? "
+Error: superpages are not supported on this machine.
+Try again without --enable-superpage.
+" "$LINENO" 5
+elif test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" != "maybe no no" -a "$enable_superpage" != no; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_SUPERPAGE 1
+_ACEOF
+
+fi
+
+@%:@ Check whether --enable-memdebug was given.
+if test "${enable_memdebug+set}" = set; then :
+  enableval=$enable_memdebug; 
+fi
+
+if test "$enable_memdebug" = yes; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_MEMDEBUG 1
+_ACEOF
+
+fi
+
+@%:@ Check whether --enable-assert was given.
+if test "${enable_assert+set}" = set; then :
+  enableval=$enable_assert; { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Use --disable-assertions instead of --disable-assert." >&5
+$as_echo "$as_me: WARNING: Use --disable-assertions instead of --disable-assert." >&2;}
+fi
+
+@%:@ Check whether --enable-assertions was given.
+if test "${enable_assertions+set}" = set; then :
+  enableval=$enable_assertions; 
+fi
+
+if test "$enable_assertions" != no -o "(" -z "$enable_assertions" -a "$enable_assert" != no ")"; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_ASSERTIONS 1
+_ACEOF
+
+fi
+
+@%:@ Check whether --enable-preconditions was given.
+if test "${enable_preconditions+set}" = set; then :
+  enableval=$enable_preconditions; 
+fi
+
+if test "$enable_preconditions" = no; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_PRECONDITIONS 0
+_ACEOF
+
+elif test -n "$enable_preconditions"; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_PRECONDITIONS 1
+_ACEOF
+
+fi
+
+@%:@ Check whether --enable-invariants was given.
+if test "${enable_invariants+set}" = set; then :
+  enableval=$enable_invariants; 
+fi
+
+if test "$enable_invariants" = no; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_INVARIANTS 0
+_ACEOF
+
+elif test -n "$enable_preconditions"; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_INVARIANTS 1
+_ACEOF
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define CACHE_LINE_SIZE 64
+_ACEOF
+
+
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_UNALIGNED_ACCESS 1
+_ACEOF
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+       cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+         mv -f confcache "$cache_file"$$ &&
+         mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+         mv -f confcache "$cache_file" ;;
+       esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIB@&t@OBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIB@&t@OBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in @%:@(
+  *posix*) :
+    set -o posix ;; @%:@(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in @%:@(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} @%:@ as_fn_error
+
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+@%:@ as_fn_executable_p FILE
+@%:@ -----------------------
+@%:@ Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} @%:@ as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by masstree-beta $as_me 0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE] 
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE] 
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+masstree-beta config.status 0.1
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../@%:@@%:@ /;s/...$/ @%:@@%:@/;p;x;p;x' <<_ASBOX
+@%:@@%:@ Running $as_me. @%:@@%:@
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "GNUmakefile") CONFIG_FILES="$CONFIG_FILES GNUmakefile" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=[    ]*/{
+h
+s///
+s/^/:/
+s/[     ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[  ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+  
+  
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/silo/masstree/autom4te.cache/output.1 b/silo/masstree/autom4te.cache/output.1
new file mode 100644 (file)
index 0000000..c764a23
--- /dev/null
@@ -0,0 +1,7594 @@
+@%:@! /bin/sh
+@%:@ Guess values for system-dependent variables and create Makefiles.
+@%:@ Generated by GNU Autoconf 2.69 for masstree-beta 0.1.
+@%:@ 
+@%:@ 
+@%:@ Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+@%:@ 
+@%:@ 
+@%:@ This configure script is free software; the Free Software Foundation
+@%:@ gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in @%:@(
+  *posix*) :
+    set -o posix ;; @%:@(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in @%:@(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in @%:@ ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in @%:@(
+  *posix*) :
+    set -o posix ;; @%:@(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+  
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+  
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in @%:@(
+        /*)
+          for as_base in sh bash ksh sh5; do
+            # Try only shells that exist, to save several forks.
+            as_shell=$as_dir/$as_base
+            if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+                   { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+                  if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+          done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+             { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in @%:@ ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+
+@%:@ as_fn_executable_p FILE
+@%:@ -----------------------
+@%:@ Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} @%:@ as_fn_executable_p
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} @%:@ as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIB@&t@OBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='masstree-beta'
+PACKAGE_TARNAME='masstree-beta'
+PACKAGE_VERSION='0.1'
+PACKAGE_STRING='masstree-beta 0.1'
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIB@&t@OBJS
+MALLOC_LIBS
+EGREP
+GREP
+CXXCPP
+ac_ct_CXX
+CXXFLAGS
+CXX
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+ac_configure_args
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_malloc
+enable_row_type
+enable_max_key_len
+enable_superpage
+enable_memdebug
+enable_assert
+enable_assertions
+enable_preconditions
+enable_invariants
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CXX
+CXXFLAGS
+CCC
+CXXCPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir runstatedir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures masstree-beta 0.1 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          @<:@@S|@ac_default_prefix@:>@
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          @<:@PREFIX@:>@
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root @<:@DATAROOTDIR/doc/masstree-beta@:>@
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of masstree-beta 0.1:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-row-type=ARG   row type: bag array array_ver str, default bag
+  --enable-max-key-len=ARG 
+                          maximum length of a key in bytes, default 255
+  --disable-superpage     disable superpage support
+  --enable-memdebug       enable memory debugging
+
+  --disable-assertions    disable debugging assertions
+  --disable-preconditions disable precondition assertions
+  --disable-invariants    disable invariant assertions
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-malloc=TYPE      memory allocator
+                          (malloc|jemalloc|tcmalloc|hoard|flow)
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  CXXCPP      C++ preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+masstree-beta configure 0.1
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+@%:@ ac_fn_c_try_compile LINENO
+@%:@ --------------------------
+@%:@ Try to compile conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_compile
+
+@%:@ ac_fn_cxx_try_compile LINENO
+@%:@ ----------------------------
+@%:@ Try to compile conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_try_compile
+
+@%:@ ac_fn_cxx_try_run LINENO
+@%:@ ------------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded. Assumes
+@%:@ that executables *can* be run.
+ac_fn_cxx_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_try_run
+
+@%:@ ac_fn_cxx_try_cpp LINENO
+@%:@ ------------------------
+@%:@ Try to preprocess conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_try_cpp
+
+@%:@ ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES
+@%:@ ---------------------------------------------------------
+@%:@ Tests whether HEADER exists and can be compiled using the include files in
+@%:@ INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_cxx_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_header_compile
+
+@%:@ ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES
+@%:@ ---------------------------------------------------------
+@%:@ Tests whether HEADER exists, giving a warning if it cannot be compiled using
+@%:@ the include files in INCLUDES and setting the cache variable VAR
+@%:@ accordingly.
+ac_fn_cxx_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@include <$2>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_header_mongrel
+
+@%:@ ac_fn_cxx_try_link LINENO
+@%:@ -------------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_try_link
+
+@%:@ ac_fn_cxx_check_type LINENO TYPE VAR INCLUDES
+@%:@ ---------------------------------------------
+@%:@ Tests whether TYPE exists after having included INCLUDES, setting cache
+@%:@ variable VAR accordingly.
+ac_fn_cxx_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+        return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+           return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_type
+
+@%:@ ac_fn_cxx_compute_int LINENO EXPR VAR INCLUDES
+@%:@ ----------------------------------------------
+@%:@ Tries to find the compile-time value of EXPR in a program that includes
+@%:@ INCLUDES, setting VAR accordingly. Returns whether the value could be
+@%:@ computed
+ac_fn_cxx_compute_int ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if test "$cross_compiling" = yes; then
+    # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= 0)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid; break
+else
+  as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+                       if test $ac_lo -le $ac_mid; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) < 0)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_lo=$ac_mid; break
+else
+  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+                       if test $ac_mid -le $ac_hi; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0;
+return test_array @<:@0@:>@;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid
+else
+  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in @%:@((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+@%:@include <stdio.h>
+@%:@include <stdlib.h>
+int
+main ()
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    return 1;
+  if (($2) < 0)
+    {
+      long int i = longval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%ld", i);
+    }
+  else
+    {
+      unsigned long int i = ulongval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%lu", i);
+    }
+  /* Do not output a trailing newline, as this causes \r\n confusion
+     on some platforms.  */
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+  echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+  ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+  fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_cxx_compute_int
+
+@%:@ ac_fn_cxx_check_decl LINENO SYMBOL VAR INCLUDES
+@%:@ -----------------------------------------------
+@%:@ Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
+@%:@ accordingly.
+ac_fn_cxx_check_decl ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  as_decl_name=`echo $2|sed 's/ *(.*//'`
+  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+@%:@ifndef $as_decl_name
+@%:@ifdef __cplusplus
+  (void) $as_decl_use;
+@%:@else
+  (void) $as_decl_name;
+@%:@endif
+@%:@endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_decl
+
+@%:@ ac_fn_cxx_check_func LINENO FUNC VAR
+@%:@ ------------------------------------
+@%:@ Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_cxx_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_cxx_check_func
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by masstree-beta $as_me 0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in @%:@((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+ac_config_files="$ac_config_files GNUmakefile"
+
+
+
+ac_user_cxx=${CXX+y}
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $@%:@ != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+  
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+  if test -n "$CCC"; then
+    CXX=$CCC
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CXX" && break
+done
+
+  if test "x$ac_ct_CXX" = x; then
+    CXX="g++"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CXX=$ac_ct_CXX
+  fi
+fi
+
+  fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+   ac_cxx_werror_flag=yes
+   ac_cv_prog_cxx_g=no
+   CXXFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+else
+  CXXFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  
+else
+  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+        CXXFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+
+$as_echo "@%:@define WORDS_BIGENDIAN_SET 1" >>confdefs.h
+
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5
+$as_echo_n "checking how to run the C++ preprocessor... " >&6; }
+if test -z "$CXXCPP"; then
+  if ${ac_cv_prog_CXXCPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CXXCPP needs to be expanded
+    for CXXCPP in "$CXX -E" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+                    Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CXXCPP=$CXXCPP
+  
+fi
+  CXXCPP=$ac_cv_prog_CXXCPP
+else
+  ac_cv_prog_CXXCPP=$CXXCPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5
+$as_echo "$CXXCPP" >&6; }
+ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+                    Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+  
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+  
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+  
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+  
+$as_echo "@%:@define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+fi
+
+done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
+if ${ac_cv_c_bigendian+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_bigendian=unknown
+    # See if we're dealing with a universal compiler.
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifndef __APPLE_CC__
+              not a universal capable compiler
+            #endif
+            typedef int dummy;
+           
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  
+       # Check for potential -arch flags.  It is not universal unless
+       # there are at least two -arch flags with different values.
+       ac_arch=
+       ac_prev=
+       for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
+        if test -n "$ac_prev"; then
+          case $ac_word in
+            i?86 | x86_64 | ppc | ppc64)
+              if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
+                ac_arch=$ac_word
+              else
+                ac_cv_c_bigendian=universal
+                break
+              fi
+              ;;
+          esac
+          ac_prev=
+        elif test "x$ac_word" = "x-arch"; then
+          ac_prev=arch
+        fi
+       done
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if sys/param.h defines the BYTE_ORDER macro.
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+            #include <sys/param.h>
+          
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+                    && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+                    && LITTLE_ENDIAN)
+             bogus endian macros
+            #endif
+          
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+               #include <sys/param.h>
+             
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+                not big endian
+               #endif
+             
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+          
+int
+main ()
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+             bogus endian macros
+            #endif
+          
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to _BIG_ENDIAN or not.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+             
+int
+main ()
+{
+#ifndef _BIG_ENDIAN
+                not big endian
+               #endif
+             
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # Compile a test program.
+      if test "$cross_compiling" = yes; then :
+  # Try to guess by grepping values from an object file.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+short int ascii_mm[] =
+                 { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+               short int ascii_ii[] =
+                 { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+               int use_ascii (int i) {
+                 return ascii_mm[i] + ascii_ii[i];
+               }
+               short int ebcdic_ii[] =
+                 { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+               short int ebcdic_mm[] =
+                 { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+               int use_ebcdic (int i) {
+                 return ebcdic_mm[i] + ebcdic_ii[i];
+               }
+               extern int foo;
+             
+int
+main ()
+{
+return use_ascii (foo) == use_ebcdic (foo);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+             ac_cv_c_bigendian=yes
+           fi
+           if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+             if test "$ac_cv_c_bigendian" = unknown; then
+               ac_cv_c_bigendian=no
+             else
+               # finding both strings is unlikely to happen, but who knows?
+               ac_cv_c_bigendian=unknown
+             fi
+           fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+
+            /* Are we little or big endian?  From Harbison&Steele.  */
+            union
+            {
+              long int l;
+              char c[sizeof (long int)];
+            } u;
+            u.l = 1;
+            return u.c[sizeof (long int) - 1] == 1;
+          
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+  ac_cv_c_bigendian=no
+else
+  ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+$as_echo "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+   yes)
+     $as_echo "@%:@define WORDS_BIGENDIAN 1" >>confdefs.h
+;; #(
+   no)
+      ;; #(
+   universal)
+       
+$as_echo "@%:@define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+
+     ;; #(
+   *)
+     as_fn_error $? "unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
+ esac
+
+
+for ac_header in sys/epoll.h numa.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+fi
+
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing numa_available" >&5
+$as_echo_n "checking for library containing numa_available... " >&6; }
+if ${ac_cv_search_numa_available+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char numa_available ();
+int
+main ()
+{
+return numa_available ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' numa; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_search_numa_available=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_numa_available+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_numa_available+:} false; then :
+  
+else
+  ac_cv_search_numa_available=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_numa_available" >&5
+$as_echo "$ac_cv_search_numa_available" >&6; }
+ac_res=$ac_cv_search_numa_available
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  
+$as_echo "@%:@define HAVE_LIBNUMA 1" >>confdefs.h
+
+fi
+
+
+
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clz builtin" >&5
+$as_echo_n "checking for __builtin_clz builtin... " >&6; }
+if ${ac_cv_have___builtin_clz+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned f(unsigned x) { return __builtin_clz(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_clz=yes
+else
+  ac_cv_have___builtin_clz=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_clz" >&5
+$as_echo "$ac_cv_have___builtin_clz" >&6; }
+    if test $ac_cv_have___builtin_clz = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CLZ 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clzl builtin" >&5
+$as_echo_n "checking for __builtin_clzl builtin... " >&6; }
+if ${ac_cv_have___builtin_clzl+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long f(unsigned long x) { return __builtin_clzl(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_clzl=yes
+else
+  ac_cv_have___builtin_clzl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_clzl" >&5
+$as_echo "$ac_cv_have___builtin_clzl" >&6; }
+    if test $ac_cv_have___builtin_clzl = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CLZL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clzll builtin" >&5
+$as_echo_n "checking for __builtin_clzll builtin... " >&6; }
+if ${ac_cv_have___builtin_clzll+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long long f(unsigned long long x) { return __builtin_clzll(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_clzll=yes
+else
+  ac_cv_have___builtin_clzll=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_clzll" >&5
+$as_echo "$ac_cv_have___builtin_clzll" >&6; }
+    if test $ac_cv_have___builtin_clzll = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CLZLL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_ctz builtin" >&5
+$as_echo_n "checking for __builtin_ctz builtin... " >&6; }
+if ${ac_cv_have___builtin_ctz+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned f(unsigned x) { return __builtin_ctz(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_ctz=yes
+else
+  ac_cv_have___builtin_ctz=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_ctz" >&5
+$as_echo "$ac_cv_have___builtin_ctz" >&6; }
+    if test $ac_cv_have___builtin_ctz = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CTZ 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_ctzl builtin" >&5
+$as_echo_n "checking for __builtin_ctzl builtin... " >&6; }
+if ${ac_cv_have___builtin_ctzl+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long f(unsigned long x) { return __builtin_ctzl(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_ctzl=yes
+else
+  ac_cv_have___builtin_ctzl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_ctzl" >&5
+$as_echo "$ac_cv_have___builtin_ctzl" >&6; }
+    if test $ac_cv_have___builtin_ctzl = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CTZL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_ctzll builtin" >&5
+$as_echo_n "checking for __builtin_ctzll builtin... " >&6; }
+if ${ac_cv_have___builtin_ctzll+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long long f(unsigned long long x) { return __builtin_ctzll(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_ctzll=yes
+else
+  ac_cv_have___builtin_ctzll=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_ctzll" >&5
+$as_echo "$ac_cv_have___builtin_ctzll" >&6; }
+    if test $ac_cv_have___builtin_ctzll = yes; then
+       
+$as_echo "@%:@define HAVE___BUILTIN_CTZLL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_synchronize builtin" >&5
+$as_echo_n "checking for __sync_synchronize builtin... " >&6; }
+if ${ac_cv_have___sync_synchronize+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long x = 11;
+    void f(long i) { long* y = &x; __sync_synchronize(); *y = i; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_synchronize=yes
+else
+  ac_cv_have___sync_synchronize=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_synchronize" >&5
+$as_echo "$ac_cv_have___sync_synchronize" >&6; }
+    if test $ac_cv_have___sync_synchronize = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_SYNCHRONIZE 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_add builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_add builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_add+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_fetch_and_add(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_add=yes
+else
+  ac_cv_have___sync_fetch_and_add=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_add" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_add" >&6; }
+    if test $ac_cv_have___sync_fetch_and_add = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_FETCH_AND_ADD 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_add_and_fetch builtin" >&5
+$as_echo_n "checking for __sync_add_and_fetch builtin... " >&6; }
+if ${ac_cv_have___sync_add_and_fetch+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_add_and_fetch(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_add_and_fetch=yes
+else
+  ac_cv_have___sync_add_and_fetch=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_add_and_fetch" >&5
+$as_echo "$ac_cv_have___sync_add_and_fetch" >&6; }
+    if test $ac_cv_have___sync_add_and_fetch = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_ADD_AND_FETCH 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_add_8 builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_add_8 builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_add_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_add(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_add_8=yes
+else
+  ac_cv_have___sync_fetch_and_add_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_add_8" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_add_8" >&6; }
+    if test $ac_cv_have___sync_fetch_and_add_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_FETCH_AND_ADD_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_add_and_fetch_8 builtin" >&5
+$as_echo_n "checking for __sync_add_and_fetch_8 builtin... " >&6; }
+if ${ac_cv_have___sync_add_and_fetch_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_add_and_fetch(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_add_and_fetch_8=yes
+else
+  ac_cv_have___sync_add_and_fetch_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_add_and_fetch_8" >&5
+$as_echo "$ac_cv_have___sync_add_and_fetch_8" >&6; }
+    if test $ac_cv_have___sync_add_and_fetch_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_ADD_AND_FETCH_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_or builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_or builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_or+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_fetch_and_or(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_or=yes
+else
+  ac_cv_have___sync_fetch_and_or=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_or" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_or" >&6; }
+    if test $ac_cv_have___sync_fetch_and_or = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_FETCH_AND_OR 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_or_and_fetch builtin" >&5
+$as_echo_n "checking for __sync_or_and_fetch builtin... " >&6; }
+if ${ac_cv_have___sync_or_and_fetch+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_or_and_fetch(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_or_and_fetch=yes
+else
+  ac_cv_have___sync_or_and_fetch=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_or_and_fetch" >&5
+$as_echo "$ac_cv_have___sync_or_and_fetch" >&6; }
+    if test $ac_cv_have___sync_or_and_fetch = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_OR_AND_FETCH 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_or_8 builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_or_8 builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_or_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_or(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_or_8=yes
+else
+  ac_cv_have___sync_fetch_and_or_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_or_8" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_or_8" >&6; }
+    if test $ac_cv_have___sync_fetch_and_or_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_FETCH_AND_OR_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_or_and_fetch_8 builtin" >&5
+$as_echo_n "checking for __sync_or_and_fetch_8 builtin... " >&6; }
+if ${ac_cv_have___sync_or_and_fetch_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_or_and_fetch(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_or_and_fetch_8=yes
+else
+  ac_cv_have___sync_or_and_fetch_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_or_and_fetch_8" >&5
+$as_echo "$ac_cv_have___sync_or_and_fetch_8" >&6; }
+    if test $ac_cv_have___sync_or_and_fetch_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_OR_AND_FETCH_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_bool_compare_and_swap builtin" >&5
+$as_echo_n "checking for __sync_bool_compare_and_swap builtin... " >&6; }
+if ${ac_cv_have___sync_bool_compare_and_swap+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+bool f(long* x, long y, long z) { return __sync_bool_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_bool_compare_and_swap=yes
+else
+  ac_cv_have___sync_bool_compare_and_swap=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_bool_compare_and_swap" >&5
+$as_echo "$ac_cv_have___sync_bool_compare_and_swap" >&6; }
+    if test $ac_cv_have___sync_bool_compare_and_swap = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_bool_compare_and_swap_8 builtin" >&5
+$as_echo_n "checking for __sync_bool_compare_and_swap_8 builtin... " >&6; }
+if ${ac_cv_have___sync_bool_compare_and_swap_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    bool f(int64_t* x, int64_t y, int64_t z) { return __sync_bool_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_bool_compare_and_swap_8=yes
+else
+  ac_cv_have___sync_bool_compare_and_swap_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_bool_compare_and_swap_8" >&5
+$as_echo "$ac_cv_have___sync_bool_compare_and_swap_8" >&6; }
+    if test $ac_cv_have___sync_bool_compare_and_swap_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_val_compare_and_swap builtin" >&5
+$as_echo_n "checking for __sync_val_compare_and_swap builtin... " >&6; }
+if ${ac_cv_have___sync_val_compare_and_swap+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x, long y, long z) { return __sync_val_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_val_compare_and_swap=yes
+else
+  ac_cv_have___sync_val_compare_and_swap=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_val_compare_and_swap" >&5
+$as_echo "$ac_cv_have___sync_val_compare_and_swap" >&6; }
+    if test $ac_cv_have___sync_val_compare_and_swap = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_val_compare_and_swap_8 builtin" >&5
+$as_echo_n "checking for __sync_val_compare_and_swap_8 builtin... " >&6; }
+if ${ac_cv_have___sync_val_compare_and_swap_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x, int64_t y, int64_t z) { return __sync_val_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_val_compare_and_swap_8=yes
+else
+  ac_cv_have___sync_val_compare_and_swap_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_val_compare_and_swap_8" >&5
+$as_echo "$ac_cv_have___sync_val_compare_and_swap_8" >&6; }
+    if test $ac_cv_have___sync_val_compare_and_swap_8 = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_lock_test_and_set builtin" >&5
+$as_echo_n "checking for __sync_lock_test_and_set builtin... " >&6; }
+if ${ac_cv_have___sync_lock_test_and_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_lock_test_and_set(x, 1); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_lock_test_and_set=yes
+else
+  ac_cv_have___sync_lock_test_and_set=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_lock_test_and_set" >&5
+$as_echo "$ac_cv_have___sync_lock_test_and_set" >&6; }
+    if test $ac_cv_have___sync_lock_test_and_set = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_LOCK_TEST_AND_SET 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_lock_test_and_set_val builtin" >&5
+$as_echo_n "checking for __sync_lock_test_and_set_val builtin... " >&6; }
+if ${ac_cv_have___sync_lock_test_and_set_val+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x, long y) { return __sync_lock_test_and_set(x, y); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_lock_test_and_set_val=yes
+else
+  ac_cv_have___sync_lock_test_and_set_val=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_lock_test_and_set_val" >&5
+$as_echo "$ac_cv_have___sync_lock_test_and_set_val" >&6; }
+    if test $ac_cv_have___sync_lock_test_and_set_val = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_lock_release_set builtin" >&5
+$as_echo_n "checking for __sync_lock_release_set builtin... " >&6; }
+if ${ac_cv_have___sync_lock_release_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+void f(long* x) { __sync_lock_release(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_lock_release_set=yes
+else
+  ac_cv_have___sync_lock_release_set=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_lock_release_set" >&5
+$as_echo "$ac_cv_have___sync_lock_release_set" >&6; }
+    if test $ac_cv_have___sync_lock_release_set = yes; then
+       
+$as_echo "@%:@define HAVE___SYNC_LOCK_RELEASE_SET 1" >>confdefs.h
+
+    fi
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands 'auto'" >&5
+$as_echo_n "checking whether the C++ compiler understands 'auto'... " >&6; }
+if ${ac_cv_cxx_auto+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_auto=yes
+else
+  ac_cv_cxx_auto=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_auto" >&5
+$as_echo "$ac_cv_cxx_auto" >&6; }
+if test "$ac_cv_cxx_auto" != yes -a -z "$ac_user_cxx"; then
+    CXX="${CXX} -std=gnu++0x"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler with -std=gnu++0x understands 'auto'" >&5
+$as_echo_n "checking whether the C++ compiler with -std=gnu++0x understands 'auto'... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_auto=yes
+else
+  ac_cv_cxx_auto=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_auto" >&5
+$as_echo "$ac_cv_cxx_auto" >&6; }
+fi
+
+if test "$ac_cv_cxx_auto" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_AUTO 1" >>confdefs.h
+
+else
+    as_fn_error $? "
+
+The C++ compiler does not appear to understand C++11.
+To fix this problem, try supplying a \"CXX\" argument to ./configure,
+such as \"./configure CXX='c++ -std=gnu++0x'\".
+
+========================================================" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands constexpr" >&5
+$as_echo_n "checking whether the C++ compiler understands constexpr... " >&6; }
+if ${ac_cv_cxx_constexpr+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+constexpr int f(int x) { return x + 1; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_constexpr=yes
+else
+  ac_cv_cxx_constexpr=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_constexpr" >&5
+$as_echo "$ac_cv_cxx_constexpr" >&6; }
+if test "$ac_cv_cxx_constexpr" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_CONSTEXPR 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands static_assert" >&5
+$as_echo_n "checking whether the C++ compiler understands static_assert... " >&6; }
+if ${ac_cv_cxx_static_assert+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+const int f = 2;
+int
+main ()
+{
+static_assert(f == 2, "f should be 2");
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_static_assert=yes
+else
+  ac_cv_cxx_static_assert=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_static_assert" >&5
+$as_echo "$ac_cv_cxx_static_assert" >&6; }
+if test "$ac_cv_cxx_static_assert" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_STATIC_ASSERT 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands rvalue references" >&5
+$as_echo_n "checking whether the C++ compiler understands rvalue references... " >&6; }
+if ${ac_cv_cxx_rvalue_references+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int f(int &) { return 1; } int f(int &&) { return 0; }
+int
+main ()
+{
+return f(int());
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_rvalue_references=yes
+else
+  ac_cv_cxx_rvalue_references=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_rvalue_references" >&5
+$as_echo "$ac_cv_cxx_rvalue_references" >&6; }
+if test "$ac_cv_cxx_rvalue_references" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_RVALUE_REFERENCES 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands template alias" >&5
+$as_echo_n "checking whether the C++ compiler understands template alias... " >&6; }
+if ${ac_cv_cxx_template_alias+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+template <typename T> struct X { typedef T type; }; template <typename T> using Y = X<T>; int f(int x) { return x; }
+int
+main ()
+{
+return f(Y<int>::type());
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_template_alias=yes
+else
+  ac_cv_cxx_template_alias=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_template_alias" >&5
+$as_echo "$ac_cv_cxx_template_alias" >&6; }
+if test "$ac_cv_cxx_template_alias" = yes; then
+    
+$as_echo "@%:@define HAVE_CXX_TEMPLATE_ALIAS 1" >>confdefs.h
+
+fi
+
+for ac_header in type_traits
+do :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "type_traits" "ac_cv_header_type_traits" "$ac_includes_default"
+if test "x$ac_cv_header_type_traits" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_TYPE_TRAITS 1
+_ACEOF
+fi
+
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::hash" >&5
+$as_echo_n "checking for std::hash... " >&6; }
+if ${ac_cv_have_std_hash+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <functional>
+#include <stddef.h>
+int
+main ()
+{
+std::hash<int> h; size_t x = h(1); return x == 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_std_hash=yes
+else
+  ac_cv_have_std_hash=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_std_hash" >&5
+$as_echo "$ac_cv_have_std_hash" >&6; }
+if test $ac_cv_have_std_hash = yes; then
+    
+$as_echo "@%:@define HAVE_STD_HASH 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __has_trivial_copy" >&5
+$as_echo_n "checking for __has_trivial_copy... " >&6; }
+if ${ac_cv_have___has_trivial_copy+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+long x = 1; if (__has_trivial_copy(long)) x = 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have___has_trivial_copy=yes
+else
+  ac_cv_have___has_trivial_copy=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___has_trivial_copy" >&5
+$as_echo "$ac_cv_have___has_trivial_copy" >&6; }
+if test $ac_cv_have___has_trivial_copy = yes; then
+    
+$as_echo "@%:@define HAVE___HAS_TRIVIAL_COPY 1" >>confdefs.h
+
+fi
+
+if test "$ac_cv_cxx_rvalue_references" = yes; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::move" >&5
+$as_echo_n "checking for std::move... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <utility>
+int
+main ()
+{
+long x = 0; long &&y = std::move(x);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_std_move=yes
+else
+  ac_cv_std_move=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_std_move" >&5
+$as_echo "$ac_cv_std_move" >&6; }
+    if test "$ac_cv_std_move" != yes; then
+        as_fn_error $? "
+
+The C++ compiler understands C++11, but does not have std::move.
+If you are using clang on Mac, ensure the -stdlib=libc++ option.
+
+========================================================" "$LINENO" 5
+    fi
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::is_trivially_copyable" >&5
+$as_echo_n "checking for std::is_trivially_copyable... " >&6; }
+if ${ac_cv_have_std_is_trivially_copyable+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <type_traits>
+int
+main ()
+{
+return std::is_trivially_copyable<int>::value;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_std_is_trivially_copyable=yes
+else
+  ac_cv_have_std_is_trivially_copyable=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_std_is_trivially_copyable" >&5
+$as_echo "$ac_cv_have_std_is_trivially_copyable" >&6; }
+if test $ac_cv_have_std_is_trivially_copyable = yes; then
+    
+$as_echo "@%:@define HAVE_STD_IS_TRIVIALLY_COPYABLE 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::is_rvalue_reference" >&5
+$as_echo_n "checking for std::is_rvalue_reference... " >&6; }
+if ${ac_cv_have_std_is_rvalue_reference+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <type_traits>
+int
+main ()
+{
+return std::is_rvalue_reference<int>::value;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_std_is_rvalue_reference=yes
+else
+  ac_cv_have_std_is_rvalue_reference=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_std_is_rvalue_reference" >&5
+$as_echo "$ac_cv_have_std_is_rvalue_reference" >&6; }
+if test $ac_cv_have_std_is_rvalue_reference = yes; then
+    
+$as_echo "@%:@define HAVE_STD_IS_RVALUE_REFERENCE 1" >>confdefs.h
+
+fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc in -lflow" >&5
+$as_echo_n "checking for malloc in -lflow... " >&6; }
+if ${ac_cv_lib_flow_malloc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lflow  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char malloc ();
+int
+main ()
+{
+return malloc ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_flow_malloc=yes
+else
+  ac_cv_lib_flow_malloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_flow_malloc" >&5
+$as_echo "$ac_cv_lib_flow_malloc" >&6; }
+if test "x$ac_cv_lib_flow_malloc" = xyes; then :
+  have_flow=true
+else
+  have_flow=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mallctl in -ljemalloc" >&5
+$as_echo_n "checking for mallctl in -ljemalloc... " >&6; }
+if ${ac_cv_lib_jemalloc_mallctl+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ljemalloc  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char mallctl ();
+int
+main ()
+{
+return mallctl ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_jemalloc_mallctl=yes
+else
+  ac_cv_lib_jemalloc_mallctl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jemalloc_mallctl" >&5
+$as_echo "$ac_cv_lib_jemalloc_mallctl" >&6; }
+if test "x$ac_cv_lib_jemalloc_mallctl" = xyes; then :
+  have_jemalloc=true
+else
+  have_jemalloc=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tc_malloc in -ltcmalloc_minimal" >&5
+$as_echo_n "checking for tc_malloc in -ltcmalloc_minimal... " >&6; }
+if ${ac_cv_lib_tcmalloc_minimal_tc_malloc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltcmalloc_minimal  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tc_malloc ();
+int
+main ()
+{
+return tc_malloc ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_tcmalloc_minimal_tc_malloc=yes
+else
+  ac_cv_lib_tcmalloc_minimal_tc_malloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tcmalloc_minimal_tc_malloc" >&5
+$as_echo "$ac_cv_lib_tcmalloc_minimal_tc_malloc" >&6; }
+if test "x$ac_cv_lib_tcmalloc_minimal_tc_malloc" = xyes; then :
+  have_tcmalloc_minimal=true
+else
+  have_tcmalloc_minimal=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Z16getMainHoardHeapv in -lhoard" >&5
+$as_echo_n "checking for _Z16getMainHoardHeapv in -lhoard... " >&6; }
+if ${ac_cv_lib_hoard__Z16getMainHoardHeapv+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lhoard  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char _Z16getMainHoardHeapv ();
+int
+main ()
+{
+return _Z16getMainHoardHeapv ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_hoard__Z16getMainHoardHeapv=yes
+else
+  ac_cv_lib_hoard__Z16getMainHoardHeapv=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_hoard__Z16getMainHoardHeapv" >&5
+$as_echo "$ac_cv_lib_hoard__Z16getMainHoardHeapv" >&6; }
+if test "x$ac_cv_lib_hoard__Z16getMainHoardHeapv" = xyes; then :
+  have_hoard=true
+else
+  have_hoard=
+fi
+
+
+
+@%:@ Check whether --with-malloc was given.
+if test "${with_malloc+set}" = set; then :
+  withval=$with_malloc; ac_mtd_malloc=$withval
+else
+  ac_mtd_malloc=yes
+fi
+
+
+if test \( "$ac_mtd_malloc" = tcmalloc -a -z "$have_tcmalloc_minimal" \) \
+       -o \( "$ac_mtd_malloc" = jemalloc -a -z "$have_jemalloc" \) \
+       -o \( "$ac_mtd_malloc" = flow -a -z "$have_flow" \) \
+        -o \( "$ac_mtd_malloc" = hoard -a -z "$have_hoard" \) ; then
+    as_fn_error $? "$ac_mtd_malloc not found" "$LINENO" 5
+elif test "$ac_mtd_malloc" = tcmalloc -o "$ac_mtd_malloc" = jemalloc -o "$ac_mtd_malloc" = flow -o "$ac_mtd_malloc" = hoard; then
+    :
+elif test "$ac_mtd_malloc" = yes -o "$ac_mtd_malloc" = default; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc library" >&5
+$as_echo_n "checking for malloc library... " >&6; }
+    if test -n "$have_flow"; then ac_mtd_malloc=flow;
+    elif test -n "$have_jemalloc"; then ac_mtd_malloc=jemalloc;
+    elif test -n "$have_tcmalloc_minimal"; then ac_mtd_malloc=tcmalloc;
+    else ac_mtd_malloc=malloc; fi
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_mtd_malloc" >&5
+$as_echo "$ac_mtd_malloc" >&6; }
+elif test "$ac_mtd_malloc" = no -o "$ac_mtd_malloc" = malloc -o -z "$ac_mtd_malloc"; then
+    ac_mtd_malloc=malloc
+else
+    as_fn_error $? "Unknown malloc type $ac_mtd_malloc" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc library" >&5
+$as_echo_n "checking for malloc library... " >&6; }
+if test "$ac_mtd_malloc" = tcmalloc; then
+    MALLOC_LIBS="-ltcmalloc_minimal"
+    
+$as_echo "@%:@define HAVE_TCMALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -ltcmalloc_minimal" >&5
+$as_echo "-ltcmalloc_minimal" >&6; }
+elif test "$ac_mtd_malloc" = jemalloc; then
+    MALLOC_LIBS="-ljemalloc"
+    
+$as_echo "@%:@define HAVE_JEMALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -ljemalloc" >&5
+$as_echo "-ljemalloc" >&6; }
+elif test "$ac_mtd_malloc" = flow; then
+    MALLOC_LIBS="-lflow"
+    
+$as_echo "@%:@define HAVE_FLOW_MALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -lflow" >&5
+$as_echo "-lflow" >&6; }
+elif test "$ac_mtd_malloc" = hoard; then
+    MALLOC_LIBS="-lhoard"
+    
+$as_echo "@%:@define HAVE_HOARD_MALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -lhoard" >&5
+$as_echo "-lhoard" >&6; }
+else
+    MALLOC_LIBS=
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: default" >&5
+$as_echo "default" >&6; }
+fi
+
+
+
+
+
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether off_t and long are the same type" >&5
+$as_echo_n "checking whether off_t and long are the same type... " >&6; }
+if ${ac_cv_have_same_type_off_t_is_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(off_t) {return 0;} int f(long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_off_t_is_long=no
+else
+  ac_cv_have_same_type_off_t_is_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_off_t_is_long" >&5
+$as_echo "$ac_cv_have_same_type_off_t_is_long" >&6; }
+    if test $ac_cv_have_same_type_off_t_is_long = yes; then
+       
+$as_echo "@%:@define HAVE_OFF_T_IS_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether off_t and long long are the same type" >&5
+$as_echo_n "checking whether off_t and long long are the same type... " >&6; }
+if ${ac_cv_have_same_type_off_t_is_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(off_t) {return 0;} int f(long long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_off_t_is_long_long=no
+else
+  ac_cv_have_same_type_off_t_is_long_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_off_t_is_long_long" >&5
+$as_echo "$ac_cv_have_same_type_off_t_is_long_long" >&6; }
+    if test $ac_cv_have_same_type_off_t_is_long_long = yes; then
+       
+$as_echo "@%:@define HAVE_OFF_T_IS_LONG_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether int64_t and long are the same type" >&5
+$as_echo_n "checking whether int64_t and long are the same type... " >&6; }
+if ${ac_cv_have_same_type_int64_t_is_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+int f(int64_t) {return 0;} int f(long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_int64_t_is_long=no
+else
+  ac_cv_have_same_type_int64_t_is_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_int64_t_is_long" >&5
+$as_echo "$ac_cv_have_same_type_int64_t_is_long" >&6; }
+    if test $ac_cv_have_same_type_int64_t_is_long = yes; then
+       
+$as_echo "@%:@define HAVE_INT64_T_IS_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether int64_t and long long are the same type" >&5
+$as_echo_n "checking whether int64_t and long long are the same type... " >&6; }
+if ${ac_cv_have_same_type_int64_t_is_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+int f(int64_t) {return 0;} int f(long long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_int64_t_is_long_long=no
+else
+  ac_cv_have_same_type_int64_t_is_long_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_int64_t_is_long_long" >&5
+$as_echo "$ac_cv_have_same_type_int64_t_is_long_long" >&6; }
+    if test $ac_cv_have_same_type_int64_t_is_long_long = yes; then
+       
+$as_echo "@%:@define HAVE_INT64_T_IS_LONG_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t and unsigned are the same type" >&5
+$as_echo_n "checking whether size_t and unsigned are the same type... " >&6; }
+if ${ac_cv_have_same_type_size_t_is_unsigned+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(size_t) {return 0;} int f(unsigned) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_size_t_is_unsigned=no
+else
+  ac_cv_have_same_type_size_t_is_unsigned=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_size_t_is_unsigned" >&5
+$as_echo "$ac_cv_have_same_type_size_t_is_unsigned" >&6; }
+    if test $ac_cv_have_same_type_size_t_is_unsigned = yes; then
+       
+$as_echo "@%:@define HAVE_SIZE_T_IS_UNSIGNED 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t and unsigned long are the same type" >&5
+$as_echo_n "checking whether size_t and unsigned long are the same type... " >&6; }
+if ${ac_cv_have_same_type_size_t_is_unsigned_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(size_t) {return 0;} int f(unsigned long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_size_t_is_unsigned_long=no
+else
+  ac_cv_have_same_type_size_t_is_unsigned_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_size_t_is_unsigned_long" >&5
+$as_echo "$ac_cv_have_same_type_size_t_is_unsigned_long" >&6; }
+    if test $ac_cv_have_same_type_size_t_is_unsigned_long = yes; then
+       
+$as_echo "@%:@define HAVE_SIZE_T_IS_UNSIGNED_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+    
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t and unsigned long long are the same type" >&5
+$as_echo_n "checking whether size_t and unsigned long long are the same type... " >&6; }
+if ${ac_cv_have_same_type_size_t_is_unsigned_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(size_t) {return 0;} int f(unsigned long long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_size_t_is_unsigned_long_long=no
+else
+  ac_cv_have_same_type_size_t_is_unsigned_long_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_size_t_is_unsigned_long_long" >&5
+$as_echo "$ac_cv_have_same_type_size_t_is_unsigned_long_long" >&6; }
+    if test $ac_cv_have_same_type_size_t_is_unsigned_long_long = yes; then
+       
+$as_echo "@%:@define HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG 1" >>confdefs.h
+
+    fi
+    
+
+
+ac_fn_cxx_check_type "$LINENO" "long long" "ac_cv_type_long_long" "$ac_includes_default"
+if test "x$ac_cv_type_long_long" = xyes; then :
+  
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_LONG_LONG 1
+_ACEOF
+
+
+fi
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of short" >&5
+$as_echo_n "checking size of short... " >&6; }
+if ${ac_cv_sizeof_short+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_short" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (short)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_short=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5
+$as_echo "$ac_cv_sizeof_short" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_SHORT $ac_cv_sizeof_short
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5
+$as_echo_n "checking size of int... " >&6; }
+if ${ac_cv_sizeof_int+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_int" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (int)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_int=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5
+$as_echo "$ac_cv_sizeof_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_INT $ac_cv_sizeof_int
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5
+$as_echo_n "checking size of long... " >&6; }
+if ${ac_cv_sizeof_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5
+$as_echo "$ac_cv_sizeof_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_LONG $ac_cv_sizeof_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5
+$as_echo_n "checking size of long long... " >&6; }
+if ${ac_cv_sizeof_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_long_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long long)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_long_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5
+$as_echo "$ac_cv_sizeof_long_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5
+$as_echo_n "checking size of void *... " >&6; }
+if ${ac_cv_sizeof_void_p+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p"        "$ac_includes_default"; then :
+  
+else
+  if test "$ac_cv_type_void_p" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (void *)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_void_p=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5
+$as_echo "$ac_cv_sizeof_void_p" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_VOID_P $ac_cv_sizeof_void_p
+_ACEOF
+
+
+
+ac_fn_cxx_check_decl "$LINENO" "getline" "ac_cv_have_decl_getline" "$ac_includes_default"
+if test "x$ac_cv_have_decl_getline" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_DECL_GETLINE $ac_have_decl
+_ACEOF
+
+
+for ac_header in time.h execinfo.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+fi
+
+done
+
+ac_fn_cxx_check_decl "$LINENO" "clock_gettime" "ac_cv_have_decl_clock_gettime" "#if HAVE_TIME_H
+# include <time.h>
+#endif
+"
+if test "x$ac_cv_have_decl_clock_gettime" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_DECL_CLOCK_GETTIME $ac_have_decl
+_ACEOF
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
+$as_echo_n "checking for library containing clock_gettime... " >&6; }
+if ${ac_cv_search_clock_gettime+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' rt; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_search_clock_gettime=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_clock_gettime+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_clock_gettime+:} false; then :
+  
+else
+  ac_cv_search_clock_gettime=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
+$as_echo "$ac_cv_search_clock_gettime" >&6; }
+ac_res=$ac_cv_search_clock_gettime
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  
+fi
+
+for ac_func in clock_gettime
+do :
+  ac_fn_cxx_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime"
+if test "x$ac_cv_func_clock_gettime" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_CLOCK_GETTIME 1
+_ACEOF
+fi
+done
+
+
+
+@%:@ Check whether --enable-row-type was given.
+if test "${enable_row_type+set}" = set; then :
+  enableval=$enable_row_type; ac_cv_row_type=$enableval
+else
+  ac_cv_row_type=bag
+fi
+
+if test "$ac_cv_row_type" = array; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_ROW_TYPE_ARRAY 1
+_ACEOF
+
+elif test "$ac_cv_row_type" = array_ver; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_ROW_TYPE_ARRAY_VER 1
+_ACEOF
+
+elif test "$ac_cv_row_type" = bag; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_ROW_TYPE_BAG 1
+_ACEOF
+
+elif test "$ac_cv_row_type" = str; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_ROW_TYPE_STR 1
+_ACEOF
+
+else
+    as_fn_error $? "$ac_cv_row_type: Unknown row type" "$LINENO" 5
+fi
+
+@%:@ Check whether --enable-max-key-len was given.
+if test "${enable_max_key_len+set}" = set; then :
+  enableval=$enable_max_key_len; ac_cv_max_key_len=$enableval
+else
+  ac_cv_max_key_len=255
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MASSTREE_MAXKEYLEN $ac_cv_max_key_len
+_ACEOF
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether MADV_HUGEPAGE is supported" >&5
+$as_echo_n "checking whether MADV_HUGEPAGE is supported... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/mman.h>
+#ifndef MADV_HUGEPAGE
+#error "no"
+#endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  have_madv_hugepage=yes
+else
+  have_madv_hugepage=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_madv_hugepage" >&5
+$as_echo "$have_madv_hugepage" >&6; }
+if test $have_madv_hugepage = yes; then
+    
+$as_echo "@%:@define HAVE_MADV_HUGEPAGE 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether MAP_HUGETLB is supported" >&5
+$as_echo_n "checking whether MAP_HUGETLB is supported... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/mman.h>
+#ifndef MAP_HUGETLB
+#error "no"
+#endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  have_map_hugetlb=yes
+else
+  have_map_hugetlb=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_map_hugetlb" >&5
+$as_echo "$have_map_hugetlb" >&6; }
+if test $have_map_hugetlb = yes; then
+    
+$as_echo "@%:@define HAVE_MAP_HUGETLB 1" >>confdefs.h
+
+fi
+
+@%:@ Check whether --enable-superpage was given.
+if test "${enable_superpage+set}" = set; then :
+  enableval=$enable_superpage; 
+else
+  enable_superpage=maybe
+fi
+
+if test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" = "yes no no"; then
+    as_fn_error $? "
+Error: superpages are not supported on this machine.
+Try again without --enable-superpage.
+" "$LINENO" 5
+elif test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" != "maybe no no" -a "$enable_superpage" != no; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_SUPERPAGE 1
+_ACEOF
+
+fi
+
+@%:@ Check whether --enable-memdebug was given.
+if test "${enable_memdebug+set}" = set; then :
+  enableval=$enable_memdebug; 
+fi
+
+if test "$enable_memdebug" = yes; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_MEMDEBUG 1
+_ACEOF
+
+fi
+
+@%:@ Check whether --enable-assert was given.
+if test "${enable_assert+set}" = set; then :
+  enableval=$enable_assert; { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Use --disable-assertions instead of --disable-assert." >&5
+$as_echo "$as_me: WARNING: Use --disable-assertions instead of --disable-assert." >&2;}
+fi
+
+@%:@ Check whether --enable-assertions was given.
+if test "${enable_assertions+set}" = set; then :
+  enableval=$enable_assertions; 
+fi
+
+if test "$enable_assertions" != no -o "(" -z "$enable_assertions" -a "$enable_assert" != no ")"; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_ASSERTIONS 1
+_ACEOF
+
+fi
+
+@%:@ Check whether --enable-preconditions was given.
+if test "${enable_preconditions+set}" = set; then :
+  enableval=$enable_preconditions; 
+fi
+
+if test "$enable_preconditions" = no; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_PRECONDITIONS 0
+_ACEOF
+
+elif test -n "$enable_preconditions"; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_PRECONDITIONS 1
+_ACEOF
+
+fi
+
+@%:@ Check whether --enable-invariants was given.
+if test "${enable_invariants+set}" = set; then :
+  enableval=$enable_invariants; 
+fi
+
+if test "$enable_invariants" = no; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_INVARIANTS 0
+_ACEOF
+
+elif test -n "$enable_preconditions"; then
+    
+cat >>confdefs.h <<_ACEOF
+@%:@define ENABLE_INVARIANTS 1
+_ACEOF
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define CACHE_LINE_SIZE 64
+_ACEOF
+
+
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_UNALIGNED_ACCESS 1
+_ACEOF
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+       cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+         mv -f confcache "$cache_file"$$ &&
+         mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+         mv -f confcache "$cache_file" ;;
+       esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIB@&t@OBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIB@&t@OBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in @%:@(
+  *posix*) :
+    set -o posix ;; @%:@(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in @%:@(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} @%:@ as_fn_error
+
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+@%:@ as_fn_executable_p FILE
+@%:@ -----------------------
+@%:@ Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} @%:@ as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by masstree-beta $as_me 0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE] 
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE] 
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+masstree-beta config.status 0.1
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../@%:@@%:@ /;s/...$/ @%:@@%:@/;p;x;p;x' <<_ASBOX
+@%:@@%:@ Running $as_me. @%:@@%:@
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "GNUmakefile") CONFIG_FILES="$CONFIG_FILES GNUmakefile" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=[    ]*/{
+h
+s///
+s/^/:/
+s/[     ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[  ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+  
+  
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/silo/masstree/autom4te.cache/requests b/silo/masstree/autom4te.cache/requests
new file mode 100644 (file)
index 0000000..e9fae7c
--- /dev/null
@@ -0,0 +1,107 @@
+# This file was generated by Autom4te Fri Oct 23 20:57:39 UTC 2015.
+# It contains the lists of macros which have been traced.
+# It can be safely removed.
+
+@request = (
+             bless( [
+                      '0',
+                      1,
+                      [
+                        '/usr/share/autoconf'
+                      ],
+                      [
+                        '/usr/share/autoconf/autoconf/autoconf.m4f',
+                        '-',
+                        '/usr/share/aclocal-1.15/internal/ac-config-macro-dirs.m4',
+                        'configure.ac'
+                      ],
+                      {
+                        'AC_DEFUN_ONCE' => 1,
+                        'AC_CONFIG_MACRO_DIR' => 1,
+                        'include' => 1,
+                        'AC_CONFIG_MACRO_DIR_TRACE' => 1,
+                        '_AM_CONFIG_MACRO_DIRS' => 1,
+                        '_m4_warn' => 1,
+                        'm4_pattern_allow' => 1,
+                        '_AM_AUTOCONF_VERSION' => 1,
+                        'AU_DEFUN' => 1,
+                        'm4_pattern_forbid' => 1,
+                        'm4_include' => 1,
+                        'KVDB_CHECK_SAME_TYPE' => 1,
+                        'AC_DEFUN' => 1,
+                        'KVDB_CHECK_BUILTIN' => 1
+                      }
+                    ], 'Autom4te::Request' ),
+             bless( [
+                      '1',
+                      1,
+                      [
+                        '/usr/share/autoconf'
+                      ],
+                      [
+                        '/usr/share/autoconf/autoconf/autoconf.m4f',
+                        'configure.ac'
+                      ],
+                      {
+                        'AC_CONFIG_AUX_DIR' => 1,
+                        'm4_pattern_allow' => 1,
+                        'AM_INIT_AUTOMAKE' => 1,
+                        'AC_CONFIG_SUBDIRS' => 1,
+                        'AM_PROG_MOC' => 1,
+                        'm4_sinclude' => 1,
+                        'AM_NLS' => 1,
+                        'AC_FC_PP_DEFINE' => 1,
+                        'AC_LIBSOURCE' => 1,
+                        'AM_ENABLE_MULTILIB' => 1,
+                        '_AM_COND_ENDIF' => 1,
+                        'AC_PROG_LIBTOOL' => 1,
+                        'AM_CONDITIONAL' => 1,
+                        '_AM_COND_IF' => 1,
+                        'AM_GNU_GETTEXT' => 1,
+                        'LT_CONFIG_LTDL_DIR' => 1,
+                        'sinclude' => 1,
+                        '_AM_SUBST_NOTMAKE' => 1,
+                        'm4_pattern_forbid' => 1,
+                        'AC_CONFIG_LINKS' => 1,
+                        'AC_SUBST_TRACE' => 1,
+                        'AC_FC_FREEFORM' => 1,
+                        'AC_CANONICAL_BUILD' => 1,
+                        'LT_SUPPORTED_TAG' => 1,
+                        '_m4_warn' => 1,
+                        'AM_POT_TOOLS' => 1,
+                        'AM_PROG_CXX_C_O' => 1,
+                        'LT_INIT' => 1,
+                        'AM_PROG_FC_C_O' => 1,
+                        'AC_DEFINE_TRACE_LITERAL' => 1,
+                        'AC_CONFIG_HEADERS' => 1,
+                        'AM_PROG_F77_C_O' => 1,
+                        'include' => 1,
+                        'AC_REQUIRE_AUX_FILE' => 1,
+                        'AM_PROG_AR' => 1,
+                        'AM_XGETTEXT_OPTION' => 1,
+                        'AC_CANONICAL_SYSTEM' => 1,
+                        'AH_OUTPUT' => 1,
+                        'AC_FC_SRCEXT' => 1,
+                        'AC_CANONICAL_TARGET' => 1,
+                        'AM_MAKEFILE_INCLUDE' => 1,
+                        'AM_MAINTAINER_MODE' => 1,
+                        '_LT_AC_TAGCONFIG' => 1,
+                        'AC_CANONICAL_HOST' => 1,
+                        'AC_CONFIG_FILES' => 1,
+                        'AM_GNU_GETTEXT_INTL_SUBDIR' => 1,
+                        'AM_PROG_CC_C_O' => 1,
+                        'AC_SUBST' => 1,
+                        '_AM_COND_ELSE' => 1,
+                        'AM_PROG_LIBTOOL' => 1,
+                        'AM_PATH_GUILE' => 1,
+                        'AC_FC_PP_SRCEXT' => 1,
+                        'AC_CONFIG_LIBOBJ_DIR' => 1,
+                        'AC_INIT' => 1,
+                        '_AM_MAKEFILE_INCLUDE' => 1,
+                        'm4_include' => 1,
+                        'AM_AUTOMAKE_VERSION' => 1,
+                        'AM_SILENT_RULES' => 1
+                      }
+                    ], 'Autom4te::Request' )
+           );
+
diff --git a/silo/masstree/autom4te.cache/traces.0 b/silo/masstree/autom4te.cache/traces.0
new file mode 100644 (file)
index 0000000..0039013
--- /dev/null
@@ -0,0 +1,211 @@
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^_?A[CHUM]_])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([_AC_])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^LIBOBJS$], [do not use LIBOBJS directly, use AC_LIBOBJ (see section `AC_LIBOBJ vs LIBOBJS'])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^AS_FLAGS$])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^_?m4_])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^dnl$])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^_?AS_])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^SHELL$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PATH_SEPARATOR$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^exec_prefix$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^prefix$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^program_transform_name$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^bindir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^sbindir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^libexecdir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^datarootdir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^datadir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^sysconfdir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^sharedstatedir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^localstatedir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^runstatedir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^includedir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^oldincludedir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^docdir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^infodir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^htmldir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^dvidir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^pdfdir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^psdir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^libdir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^mandir$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^DEFS$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^ECHO_C$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^ECHO_N$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^ECHO_T$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^build_alias$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^host_alias$])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^target_alias$])
+m4trace:configure.ac:7: -1- m4_pattern_allow([^ac_configure_args$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CFLAGS$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^LDFLAGS$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^ac_ct_CC$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^EXEEXT$])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^OBJEXT$])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^CXX$])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^CXXFLAGS$])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^LDFLAGS$])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^CXX$])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^ac_ct_CXX$])
+m4trace:configure.ac:13: -1- _m4_warn([obsolete], [The macro `AC_LANG_CPLUSPLUS' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/c.m4:252: AC_LANG_CPLUSPLUS is expanded from...
+configure.ac:13: the top level])
+m4trace:configure.ac:15: -1- m4_pattern_allow([^WORDS_BIGENDIAN_SET$])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^CXXCPP$])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^CXXCPP$])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^GREP$])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^EGREP$])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^STDC_HEADERS$])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^WORDS_BIGENDIAN$])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^AC_APPLE_UNIVERSAL_BUILD$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^HAVE_LIBNUMA$])
+m4trace:configure.ac:25: -1- AC_DEFUN([KVDB_CHECK_BUILTIN], [
+    AC_CACHE_CHECK([for $1 builtin], [ac_cv_have_$1],
+       [AC_LINK_IFELSE([AC_LANG_PROGRAM([$2], [])],
+           [ac_cv_have_$1=yes], [ac_cv_have_$1=no])])
+    if test $ac_cv_have_$1 = yes; then
+       AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1], [Define if you have the $1 builtin.])
+    fi
+])
+m4trace:configure.ac:34: -1- KVDB_CHECK_BUILTIN([__builtin_clz], [[unsigned f(unsigned x) { return __builtin_clz(x); }]])
+m4trace:configure.ac:34: -1- m4_pattern_allow([^HAVE___BUILTIN_CLZ$])
+m4trace:configure.ac:37: -1- KVDB_CHECK_BUILTIN([__builtin_clzl], [[unsigned long f(unsigned long x) { return __builtin_clzl(x); }]])
+m4trace:configure.ac:37: -1- m4_pattern_allow([^HAVE___BUILTIN_CLZL$])
+m4trace:configure.ac:40: -1- KVDB_CHECK_BUILTIN([__builtin_clzll], [[unsigned long long f(unsigned long long x) { return __builtin_clzll(x); }]])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^HAVE___BUILTIN_CLZLL$])
+m4trace:configure.ac:43: -1- KVDB_CHECK_BUILTIN([__builtin_ctz], [[unsigned f(unsigned x) { return __builtin_ctz(x); }]])
+m4trace:configure.ac:43: -1- m4_pattern_allow([^HAVE___BUILTIN_CTZ$])
+m4trace:configure.ac:46: -1- KVDB_CHECK_BUILTIN([__builtin_ctzl], [[unsigned long f(unsigned long x) { return __builtin_ctzl(x); }]])
+m4trace:configure.ac:46: -1- m4_pattern_allow([^HAVE___BUILTIN_CTZL$])
+m4trace:configure.ac:49: -1- KVDB_CHECK_BUILTIN([__builtin_ctzll], [[unsigned long long f(unsigned long long x) { return __builtin_ctzll(x); }]])
+m4trace:configure.ac:49: -1- m4_pattern_allow([^HAVE___BUILTIN_CTZLL$])
+m4trace:configure.ac:52: -1- KVDB_CHECK_BUILTIN([__sync_synchronize], [[long x = 11;
+    void f(long i) { long* y = &x; __sync_synchronize(); *y = i; }]])
+m4trace:configure.ac:52: -1- m4_pattern_allow([^HAVE___SYNC_SYNCHRONIZE$])
+m4trace:configure.ac:55: -1- KVDB_CHECK_BUILTIN([__sync_fetch_and_add], [[long f(long* x) { return __sync_fetch_and_add(x, 2L); }]])
+m4trace:configure.ac:55: -1- m4_pattern_allow([^HAVE___SYNC_FETCH_AND_ADD$])
+m4trace:configure.ac:58: -1- KVDB_CHECK_BUILTIN([__sync_add_and_fetch], [[long f(long* x) { return __sync_add_and_fetch(x, 2L); }]])
+m4trace:configure.ac:58: -1- m4_pattern_allow([^HAVE___SYNC_ADD_AND_FETCH$])
+m4trace:configure.ac:61: -1- KVDB_CHECK_BUILTIN([__sync_fetch_and_add_8], [[#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_add(x, (int64_t) 2); }]])
+m4trace:configure.ac:61: -1- m4_pattern_allow([^HAVE___SYNC_FETCH_AND_ADD_8$])
+m4trace:configure.ac:65: -1- KVDB_CHECK_BUILTIN([__sync_add_and_fetch_8], [[#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_add_and_fetch(x, (int64_t) 2); }]])
+m4trace:configure.ac:65: -1- m4_pattern_allow([^HAVE___SYNC_ADD_AND_FETCH_8$])
+m4trace:configure.ac:69: -1- KVDB_CHECK_BUILTIN([__sync_fetch_and_or], [[long f(long* x) { return __sync_fetch_and_or(x, 2L); }]])
+m4trace:configure.ac:69: -1- m4_pattern_allow([^HAVE___SYNC_FETCH_AND_OR$])
+m4trace:configure.ac:72: -1- KVDB_CHECK_BUILTIN([__sync_or_and_fetch], [[long f(long* x) { return __sync_or_and_fetch(x, 2L); }]])
+m4trace:configure.ac:72: -1- m4_pattern_allow([^HAVE___SYNC_OR_AND_FETCH$])
+m4trace:configure.ac:75: -1- KVDB_CHECK_BUILTIN([__sync_fetch_and_or_8], [[#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_or(x, (int64_t) 2); }]])
+m4trace:configure.ac:75: -1- m4_pattern_allow([^HAVE___SYNC_FETCH_AND_OR_8$])
+m4trace:configure.ac:79: -1- KVDB_CHECK_BUILTIN([__sync_or_and_fetch_8], [[#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_or_and_fetch(x, (int64_t) 2); }]])
+m4trace:configure.ac:79: -1- m4_pattern_allow([^HAVE___SYNC_OR_AND_FETCH_8$])
+m4trace:configure.ac:83: -1- KVDB_CHECK_BUILTIN([__sync_bool_compare_and_swap], [[bool f(long* x, long y, long z) { return __sync_bool_compare_and_swap(x, y, z); }]])
+m4trace:configure.ac:83: -1- m4_pattern_allow([^HAVE___SYNC_BOOL_COMPARE_AND_SWAP$])
+m4trace:configure.ac:86: -1- KVDB_CHECK_BUILTIN([__sync_bool_compare_and_swap_8], [[#include <stdint.h>
+    bool f(int64_t* x, int64_t y, int64_t z) { return __sync_bool_compare_and_swap(x, y, z); }]])
+m4trace:configure.ac:86: -1- m4_pattern_allow([^HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8$])
+m4trace:configure.ac:90: -1- KVDB_CHECK_BUILTIN([__sync_val_compare_and_swap], [[long f(long* x, long y, long z) { return __sync_val_compare_and_swap(x, y, z); }]])
+m4trace:configure.ac:90: -1- m4_pattern_allow([^HAVE___SYNC_VAL_COMPARE_AND_SWAP$])
+m4trace:configure.ac:93: -1- KVDB_CHECK_BUILTIN([__sync_val_compare_and_swap_8], [[#include <stdint.h>
+    int64_t f(int64_t* x, int64_t y, int64_t z) { return __sync_val_compare_and_swap(x, y, z); }]])
+m4trace:configure.ac:93: -1- m4_pattern_allow([^HAVE___SYNC_VAL_COMPARE_AND_SWAP_8$])
+m4trace:configure.ac:97: -1- KVDB_CHECK_BUILTIN([__sync_lock_test_and_set], [[long f(long* x) { return __sync_lock_test_and_set(x, 1); }]])
+m4trace:configure.ac:97: -1- m4_pattern_allow([^HAVE___SYNC_LOCK_TEST_AND_SET$])
+m4trace:configure.ac:100: -1- KVDB_CHECK_BUILTIN([__sync_lock_test_and_set_val], [[long f(long* x, long y) { return __sync_lock_test_and_set(x, y); }]])
+m4trace:configure.ac:100: -1- m4_pattern_allow([^HAVE___SYNC_LOCK_TEST_AND_SET_VAL$])
+m4trace:configure.ac:103: -1- KVDB_CHECK_BUILTIN([__sync_lock_release_set], [[void f(long* x) { __sync_lock_release(x); }]])
+m4trace:configure.ac:103: -1- m4_pattern_allow([^HAVE___SYNC_LOCK_RELEASE_SET$])
+m4trace:configure.ac:121: -1- m4_pattern_allow([^HAVE_CXX_AUTO$])
+m4trace:configure.ac:136: -1- m4_pattern_allow([^HAVE_CXX_CONSTEXPR$])
+m4trace:configure.ac:143: -1- m4_pattern_allow([^HAVE_CXX_STATIC_ASSERT$])
+m4trace:configure.ac:150: -1- m4_pattern_allow([^HAVE_CXX_RVALUE_REFERENCES$])
+m4trace:configure.ac:157: -1- m4_pattern_allow([^HAVE_CXX_TEMPLATE_ALIAS$])
+m4trace:configure.ac:160: -1- m4_pattern_allow([^HAVE_TYPE_TRAITS$])
+m4trace:configure.ac:168: -1- m4_pattern_allow([^HAVE_STD_HASH$])
+m4trace:configure.ac:174: -1- m4_pattern_allow([^HAVE___HAS_TRIVIAL_COPY$])
+m4trace:configure.ac:194: -1- m4_pattern_allow([^HAVE_STD_IS_TRIVIALLY_COPYABLE$])
+m4trace:configure.ac:200: -1- m4_pattern_allow([^HAVE_STD_IS_RVALUE_REFERENCE$])
+m4trace:configure.ac:239: -1- m4_pattern_allow([^HAVE_TCMALLOC$])
+m4trace:configure.ac:243: -1- m4_pattern_allow([^HAVE_JEMALLOC$])
+m4trace:configure.ac:247: -1- m4_pattern_allow([^HAVE_FLOW_MALLOC$])
+m4trace:configure.ac:251: -1- m4_pattern_allow([^HAVE_HOARD_MALLOC$])
+m4trace:configure.ac:257: -1- m4_pattern_allow([^MALLOC_LIBS$])
+m4trace:configure.ac:262: -1- AC_DEFUN([KVDB_CHECK_SAME_TYPE], [
+    pushdef([KVDB_CST_VAR], [AS_TR_SH([ac_cv_have_same_type_$1_is_$2])])
+    AC_CACHE_CHECK([whether $1 and $2 are the same type], KVDB_CST_VAR,
+       [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([$3
+int f($1) {return 0;} int f($2) {return 0;}], [])],
+           [KVDB_CST_VAR=no], [KVDB_CST_VAR=yes])])
+    if test $KVDB_CST_VAR = yes; then
+       AC_DEFINE(AS_TR_CPP([HAVE_$1_IS_$2]), [1], [Define if $1 and $2 are the same type.])
+    fi
+    popdef([KVDB_CST_VAR])
+])
+m4trace:configure.ac:274: -1- KVDB_CHECK_SAME_TYPE([off_t], [long], [#include <stdio.h>])
+m4trace:configure.ac:274: -1- m4_pattern_allow([^HAVE_OFF_T_IS_LONG$])
+m4trace:configure.ac:275: -1- KVDB_CHECK_SAME_TYPE([off_t], [long long], [#include <stdio.h>])
+m4trace:configure.ac:275: -1- m4_pattern_allow([^HAVE_OFF_T_IS_LONG_LONG$])
+m4trace:configure.ac:276: -1- KVDB_CHECK_SAME_TYPE([int64_t], [long], [#include <stdint.h>])
+m4trace:configure.ac:276: -1- m4_pattern_allow([^HAVE_INT64_T_IS_LONG$])
+m4trace:configure.ac:277: -1- KVDB_CHECK_SAME_TYPE([int64_t], [long long], [#include <stdint.h>])
+m4trace:configure.ac:277: -1- m4_pattern_allow([^HAVE_INT64_T_IS_LONG_LONG$])
+m4trace:configure.ac:278: -1- KVDB_CHECK_SAME_TYPE([size_t], [unsigned], [#include <stdio.h>])
+m4trace:configure.ac:278: -1- m4_pattern_allow([^HAVE_SIZE_T_IS_UNSIGNED$])
+m4trace:configure.ac:279: -1- KVDB_CHECK_SAME_TYPE([size_t], [unsigned long], [#include <stdio.h>])
+m4trace:configure.ac:279: -1- m4_pattern_allow([^HAVE_SIZE_T_IS_UNSIGNED_LONG$])
+m4trace:configure.ac:280: -1- KVDB_CHECK_SAME_TYPE([size_t], [unsigned long long], [#include <stdio.h>])
+m4trace:configure.ac:280: -1- m4_pattern_allow([^HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG$])
+m4trace:configure.ac:282: -1- m4_pattern_allow([^HAVE_LONG_LONG$])
+m4trace:configure.ac:283: -1- m4_pattern_allow([^SIZEOF_SHORT$])
+m4trace:configure.ac:284: -1- m4_pattern_allow([^SIZEOF_INT$])
+m4trace:configure.ac:285: -1- m4_pattern_allow([^SIZEOF_LONG$])
+m4trace:configure.ac:286: -1- m4_pattern_allow([^SIZEOF_LONG_LONG$])
+m4trace:configure.ac:287: -1- m4_pattern_allow([^SIZEOF_VOID_P$])
+m4trace:configure.ac:289: -1- m4_pattern_allow([^HAVE_DECL_GETLINE$])
+m4trace:configure.ac:292: -1- m4_pattern_allow([^HAVE_DECL_CLOCK_GETTIME$])
+m4trace:configure.ac:296: -1- m4_pattern_allow([^HAVE_CLOCK_GETTIME$])
+m4trace:configure.ac:304: -1- m4_pattern_allow([^MASSTREE_ROW_TYPE_ARRAY$])
+m4trace:configure.ac:306: -1- m4_pattern_allow([^MASSTREE_ROW_TYPE_ARRAY_VER$])
+m4trace:configure.ac:308: -1- m4_pattern_allow([^MASSTREE_ROW_TYPE_BAG$])
+m4trace:configure.ac:310: -1- m4_pattern_allow([^MASSTREE_ROW_TYPE_STR$])
+m4trace:configure.ac:319: -1- m4_pattern_allow([^MASSTREE_MAXKEYLEN$])
+m4trace:configure.ac:329: -1- m4_pattern_allow([^HAVE_MADV_HUGEPAGE$])
+m4trace:configure.ac:340: -1- m4_pattern_allow([^HAVE_MAP_HUGETLB$])
+m4trace:configure.ac:353: -1- m4_pattern_allow([^HAVE_SUPERPAGE$])
+m4trace:configure.ac:360: -1- m4_pattern_allow([^HAVE_MEMDEBUG$])
+m4trace:configure.ac:370: -1- m4_pattern_allow([^ENABLE_ASSERTIONS$])
+m4trace:configure.ac:377: -1- m4_pattern_allow([^ENABLE_PRECONDITIONS$])
+m4trace:configure.ac:379: -1- m4_pattern_allow([^ENABLE_PRECONDITIONS$])
+m4trace:configure.ac:386: -1- m4_pattern_allow([^ENABLE_INVARIANTS$])
+m4trace:configure.ac:388: -1- m4_pattern_allow([^ENABLE_INVARIANTS$])
+m4trace:configure.ac:391: -1- m4_pattern_allow([^CACHE_LINE_SIZE$])
+m4trace:configure.ac:436: -1- m4_pattern_allow([^HAVE_UNALIGNED_ACCESS$])
+m4trace:configure.ac:438: -1- m4_pattern_allow([^LIB@&t@OBJS$])
+m4trace:configure.ac:438: -1- m4_pattern_allow([^LTLIBOBJS$])
diff --git a/silo/masstree/autom4te.cache/traces.1 b/silo/masstree/autom4te.cache/traces.1
new file mode 100644 (file)
index 0000000..95c6294
--- /dev/null
@@ -0,0 +1,619 @@
+m4trace:configure.ac:3: -1- AC_INIT([masstree-beta], [0.1])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^_?A[CHUM]_])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([_AC_])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^LIBOBJS$], [do not use LIBOBJS directly, use AC_LIBOBJ (see section `AC_LIBOBJ vs LIBOBJS'])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^AS_FLAGS$])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^_?m4_])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^dnl$])
+m4trace:configure.ac:3: -1- m4_pattern_forbid([^_?AS_])
+m4trace:configure.ac:3: -1- AC_SUBST([SHELL])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([SHELL])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^SHELL$])
+m4trace:configure.ac:3: -1- AC_SUBST([PATH_SEPARATOR])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([PATH_SEPARATOR])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PATH_SEPARATOR$])
+m4trace:configure.ac:3: -1- AC_SUBST([PACKAGE_NAME], [m4_ifdef([AC_PACKAGE_NAME],      ['AC_PACKAGE_NAME'])])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([PACKAGE_NAME])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:3: -1- AC_SUBST([PACKAGE_TARNAME], [m4_ifdef([AC_PACKAGE_TARNAME],   ['AC_PACKAGE_TARNAME'])])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([PACKAGE_TARNAME])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:3: -1- AC_SUBST([PACKAGE_VERSION], [m4_ifdef([AC_PACKAGE_VERSION],   ['AC_PACKAGE_VERSION'])])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([PACKAGE_VERSION])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:3: -1- AC_SUBST([PACKAGE_STRING], [m4_ifdef([AC_PACKAGE_STRING],    ['AC_PACKAGE_STRING'])])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([PACKAGE_STRING])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:3: -1- AC_SUBST([PACKAGE_BUGREPORT], [m4_ifdef([AC_PACKAGE_BUGREPORT], ['AC_PACKAGE_BUGREPORT'])])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([PACKAGE_BUGREPORT])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:3: -1- AC_SUBST([PACKAGE_URL], [m4_ifdef([AC_PACKAGE_URL],       ['AC_PACKAGE_URL'])])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([PACKAGE_URL])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:3: -1- AC_SUBST([exec_prefix], [NONE])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([exec_prefix])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^exec_prefix$])
+m4trace:configure.ac:3: -1- AC_SUBST([prefix], [NONE])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([prefix])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^prefix$])
+m4trace:configure.ac:3: -1- AC_SUBST([program_transform_name], [s,x,x,])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([program_transform_name])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^program_transform_name$])
+m4trace:configure.ac:3: -1- AC_SUBST([bindir], ['${exec_prefix}/bin'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([bindir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^bindir$])
+m4trace:configure.ac:3: -1- AC_SUBST([sbindir], ['${exec_prefix}/sbin'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([sbindir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^sbindir$])
+m4trace:configure.ac:3: -1- AC_SUBST([libexecdir], ['${exec_prefix}/libexec'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([libexecdir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^libexecdir$])
+m4trace:configure.ac:3: -1- AC_SUBST([datarootdir], ['${prefix}/share'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([datarootdir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^datarootdir$])
+m4trace:configure.ac:3: -1- AC_SUBST([datadir], ['${datarootdir}'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([datadir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^datadir$])
+m4trace:configure.ac:3: -1- AC_SUBST([sysconfdir], ['${prefix}/etc'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([sysconfdir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^sysconfdir$])
+m4trace:configure.ac:3: -1- AC_SUBST([sharedstatedir], ['${prefix}/com'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([sharedstatedir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^sharedstatedir$])
+m4trace:configure.ac:3: -1- AC_SUBST([localstatedir], ['${prefix}/var'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([localstatedir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^localstatedir$])
+m4trace:configure.ac:3: -1- AC_SUBST([runstatedir], ['${localstatedir}/run'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([runstatedir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^runstatedir$])
+m4trace:configure.ac:3: -1- AC_SUBST([includedir], ['${prefix}/include'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([includedir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^includedir$])
+m4trace:configure.ac:3: -1- AC_SUBST([oldincludedir], ['/usr/include'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([oldincludedir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^oldincludedir$])
+m4trace:configure.ac:3: -1- AC_SUBST([docdir], [m4_ifset([AC_PACKAGE_TARNAME],
+                                    ['${datarootdir}/doc/${PACKAGE_TARNAME}'],
+                                    ['${datarootdir}/doc/${PACKAGE}'])])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([docdir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^docdir$])
+m4trace:configure.ac:3: -1- AC_SUBST([infodir], ['${datarootdir}/info'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([infodir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^infodir$])
+m4trace:configure.ac:3: -1- AC_SUBST([htmldir], ['${docdir}'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([htmldir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^htmldir$])
+m4trace:configure.ac:3: -1- AC_SUBST([dvidir], ['${docdir}'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([dvidir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^dvidir$])
+m4trace:configure.ac:3: -1- AC_SUBST([pdfdir], ['${docdir}'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([pdfdir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^pdfdir$])
+m4trace:configure.ac:3: -1- AC_SUBST([psdir], ['${docdir}'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([psdir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^psdir$])
+m4trace:configure.ac:3: -1- AC_SUBST([libdir], ['${exec_prefix}/lib'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([libdir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^libdir$])
+m4trace:configure.ac:3: -1- AC_SUBST([localedir], ['${datarootdir}/locale'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([localedir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:3: -1- AC_SUBST([mandir], ['${datarootdir}/man'])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([mandir])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^mandir$])
+m4trace:configure.ac:3: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_NAME])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:3: -1- AH_OUTPUT([PACKAGE_NAME], [/* Define to the full name of this package. */
+@%:@undef PACKAGE_NAME])
+m4trace:configure.ac:3: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_TARNAME])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:3: -1- AH_OUTPUT([PACKAGE_TARNAME], [/* Define to the one symbol short name of this package. */
+@%:@undef PACKAGE_TARNAME])
+m4trace:configure.ac:3: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_VERSION])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:3: -1- AH_OUTPUT([PACKAGE_VERSION], [/* Define to the version of this package. */
+@%:@undef PACKAGE_VERSION])
+m4trace:configure.ac:3: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_STRING])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:3: -1- AH_OUTPUT([PACKAGE_STRING], [/* Define to the full name and version of this package. */
+@%:@undef PACKAGE_STRING])
+m4trace:configure.ac:3: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_BUGREPORT])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:3: -1- AH_OUTPUT([PACKAGE_BUGREPORT], [/* Define to the address where bug reports for this package should be sent. */
+@%:@undef PACKAGE_BUGREPORT])
+m4trace:configure.ac:3: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_URL])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:3: -1- AH_OUTPUT([PACKAGE_URL], [/* Define to the home page for this package. */
+@%:@undef PACKAGE_URL])
+m4trace:configure.ac:3: -1- AC_SUBST([DEFS])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([DEFS])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^DEFS$])
+m4trace:configure.ac:3: -1- AC_SUBST([ECHO_C])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([ECHO_C])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^ECHO_C$])
+m4trace:configure.ac:3: -1- AC_SUBST([ECHO_N])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([ECHO_N])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^ECHO_N$])
+m4trace:configure.ac:3: -1- AC_SUBST([ECHO_T])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([ECHO_T])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^ECHO_T$])
+m4trace:configure.ac:3: -1- AC_SUBST([LIBS])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([LIBS])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:3: -1- AC_SUBST([build_alias])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([build_alias])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^build_alias$])
+m4trace:configure.ac:3: -1- AC_SUBST([host_alias])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([host_alias])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^host_alias$])
+m4trace:configure.ac:3: -1- AC_SUBST([target_alias])
+m4trace:configure.ac:3: -1- AC_SUBST_TRACE([target_alias])
+m4trace:configure.ac:3: -1- m4_pattern_allow([^target_alias$])
+m4trace:configure.ac:5: -1- AC_CONFIG_HEADERS([config.h])
+m4trace:configure.ac:6: -1- AC_CONFIG_FILES([GNUmakefile])
+m4trace:configure.ac:7: -1- AC_SUBST([ac_configure_args])
+m4trace:configure.ac:7: -1- AC_SUBST_TRACE([ac_configure_args])
+m4trace:configure.ac:7: -1- m4_pattern_allow([^ac_configure_args$])
+m4trace:configure.ac:11: -1- AC_SUBST([CC])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- AC_SUBST([CFLAGS])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([CFLAGS])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CFLAGS$])
+m4trace:configure.ac:11: -1- AC_SUBST([LDFLAGS])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([LDFLAGS])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^LDFLAGS$])
+m4trace:configure.ac:11: -1- AC_SUBST([LIBS])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([LIBS])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:11: -1- AC_SUBST([CPPFLAGS])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([CPPFLAGS])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:11: -1- AC_SUBST([CC])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- AC_SUBST([CC])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- AC_SUBST([CC])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- AC_SUBST([CC])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:11: -1- AC_SUBST([ac_ct_CC])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([ac_ct_CC])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^ac_ct_CC$])
+m4trace:configure.ac:11: -1- AC_SUBST([EXEEXT], [$ac_cv_exeext])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([EXEEXT])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^EXEEXT$])
+m4trace:configure.ac:11: -1- AC_SUBST([OBJEXT], [$ac_cv_objext])
+m4trace:configure.ac:11: -1- AC_SUBST_TRACE([OBJEXT])
+m4trace:configure.ac:11: -1- m4_pattern_allow([^OBJEXT$])
+m4trace:configure.ac:12: -1- AC_SUBST([CXX])
+m4trace:configure.ac:12: -1- AC_SUBST_TRACE([CXX])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^CXX$])
+m4trace:configure.ac:12: -1- AC_SUBST([CXXFLAGS])
+m4trace:configure.ac:12: -1- AC_SUBST_TRACE([CXXFLAGS])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^CXXFLAGS$])
+m4trace:configure.ac:12: -1- AC_SUBST([LDFLAGS])
+m4trace:configure.ac:12: -1- AC_SUBST_TRACE([LDFLAGS])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^LDFLAGS$])
+m4trace:configure.ac:12: -1- AC_SUBST([LIBS])
+m4trace:configure.ac:12: -1- AC_SUBST_TRACE([LIBS])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:12: -1- AC_SUBST([CPPFLAGS])
+m4trace:configure.ac:12: -1- AC_SUBST_TRACE([CPPFLAGS])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:12: -1- AC_SUBST([CXX])
+m4trace:configure.ac:12: -1- AC_SUBST_TRACE([CXX])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^CXX$])
+m4trace:configure.ac:12: -1- AC_SUBST([ac_ct_CXX])
+m4trace:configure.ac:12: -1- AC_SUBST_TRACE([ac_ct_CXX])
+m4trace:configure.ac:12: -1- m4_pattern_allow([^ac_ct_CXX$])
+m4trace:configure.ac:13: -1- _m4_warn([obsolete], [The macro `AC_LANG_CPLUSPLUS' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/c.m4:252: AC_LANG_CPLUSPLUS is expanded from...
+configure.ac:13: the top level])
+m4trace:configure.ac:15: -1- AC_DEFINE_TRACE_LITERAL([WORDS_BIGENDIAN_SET])
+m4trace:configure.ac:15: -1- m4_pattern_allow([^WORDS_BIGENDIAN_SET$])
+m4trace:configure.ac:15: -1- AH_OUTPUT([WORDS_BIGENDIAN_SET], [/* Define if WORDS_BIGENDIAN has been set. */
+@%:@undef WORDS_BIGENDIAN_SET])
+m4trace:configure.ac:16: -1- AH_OUTPUT([WORDS_BIGENDIAN], [/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+#  undef WORDS_BIGENDIAN
+# endif
+#endif])
+m4trace:configure.ac:16: -1- AC_SUBST([CXXCPP])
+m4trace:configure.ac:16: -1- AC_SUBST_TRACE([CXXCPP])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^CXXCPP$])
+m4trace:configure.ac:16: -1- AC_SUBST([CPPFLAGS])
+m4trace:configure.ac:16: -1- AC_SUBST_TRACE([CPPFLAGS])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:16: -1- AC_SUBST([CXXCPP])
+m4trace:configure.ac:16: -1- AC_SUBST_TRACE([CXXCPP])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^CXXCPP$])
+m4trace:configure.ac:16: -1- AC_SUBST([GREP])
+m4trace:configure.ac:16: -1- AC_SUBST_TRACE([GREP])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^GREP$])
+m4trace:configure.ac:16: -1- AC_SUBST([EGREP])
+m4trace:configure.ac:16: -1- AC_SUBST_TRACE([EGREP])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^EGREP$])
+m4trace:configure.ac:16: -1- AC_DEFINE_TRACE_LITERAL([STDC_HEADERS])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^STDC_HEADERS$])
+m4trace:configure.ac:16: -1- AH_OUTPUT([STDC_HEADERS], [/* Define to 1 if you have the ANSI C header files. */
+@%:@undef STDC_HEADERS])
+m4trace:configure.ac:16: -1- AH_OUTPUT([HAVE_SYS_TYPES_H], [/* Define to 1 if you have the <sys/types.h> header file. */
+@%:@undef HAVE_SYS_TYPES_H])
+m4trace:configure.ac:16: -1- AH_OUTPUT([HAVE_SYS_STAT_H], [/* Define to 1 if you have the <sys/stat.h> header file. */
+@%:@undef HAVE_SYS_STAT_H])
+m4trace:configure.ac:16: -1- AH_OUTPUT([HAVE_STDLIB_H], [/* Define to 1 if you have the <stdlib.h> header file. */
+@%:@undef HAVE_STDLIB_H])
+m4trace:configure.ac:16: -1- AH_OUTPUT([HAVE_STRING_H], [/* Define to 1 if you have the <string.h> header file. */
+@%:@undef HAVE_STRING_H])
+m4trace:configure.ac:16: -1- AH_OUTPUT([HAVE_MEMORY_H], [/* Define to 1 if you have the <memory.h> header file. */
+@%:@undef HAVE_MEMORY_H])
+m4trace:configure.ac:16: -1- AH_OUTPUT([HAVE_STRINGS_H], [/* Define to 1 if you have the <strings.h> header file. */
+@%:@undef HAVE_STRINGS_H])
+m4trace:configure.ac:16: -1- AH_OUTPUT([HAVE_INTTYPES_H], [/* Define to 1 if you have the <inttypes.h> header file. */
+@%:@undef HAVE_INTTYPES_H])
+m4trace:configure.ac:16: -1- AH_OUTPUT([HAVE_STDINT_H], [/* Define to 1 if you have the <stdint.h> header file. */
+@%:@undef HAVE_STDINT_H])
+m4trace:configure.ac:16: -1- AH_OUTPUT([HAVE_UNISTD_H], [/* Define to 1 if you have the <unistd.h> header file. */
+@%:@undef HAVE_UNISTD_H])
+m4trace:configure.ac:16: -1- AC_DEFINE_TRACE_LITERAL([WORDS_BIGENDIAN])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^WORDS_BIGENDIAN$])
+m4trace:configure.ac:16: -1- AC_DEFINE_TRACE_LITERAL([AC_APPLE_UNIVERSAL_BUILD])
+m4trace:configure.ac:16: -1- m4_pattern_allow([^AC_APPLE_UNIVERSAL_BUILD$])
+m4trace:configure.ac:16: -1- AH_OUTPUT([AC_APPLE_UNIVERSAL_BUILD], [/* Define if building universal (internal helper macro) */
+@%:@undef AC_APPLE_UNIVERSAL_BUILD])
+m4trace:configure.ac:18: -1- AH_OUTPUT([HAVE_SYS_EPOLL_H], [/* Define to 1 if you have the <sys/epoll.h> header file. */
+@%:@undef HAVE_SYS_EPOLL_H])
+m4trace:configure.ac:18: -1- AH_OUTPUT([HAVE_NUMA_H], [/* Define to 1 if you have the <numa.h> header file. */
+@%:@undef HAVE_NUMA_H])
+m4trace:configure.ac:20: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LIBNUMA])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^HAVE_LIBNUMA$])
+m4trace:configure.ac:20: -1- AH_OUTPUT([HAVE_LIBNUMA], [/* Define if you have libnuma. */
+@%:@undef HAVE_LIBNUMA])
+m4trace:configure.ac:34: -1- AC_DEFINE_TRACE_LITERAL([HAVE___BUILTIN_CLZ])
+m4trace:configure.ac:34: -1- m4_pattern_allow([^HAVE___BUILTIN_CLZ$])
+m4trace:configure.ac:34: -1- AH_OUTPUT([HAVE___BUILTIN_CLZ], [/* Define if you have the __builtin_clz builtin. */
+@%:@undef HAVE___BUILTIN_CLZ])
+m4trace:configure.ac:37: -1- AC_DEFINE_TRACE_LITERAL([HAVE___BUILTIN_CLZL])
+m4trace:configure.ac:37: -1- m4_pattern_allow([^HAVE___BUILTIN_CLZL$])
+m4trace:configure.ac:37: -1- AH_OUTPUT([HAVE___BUILTIN_CLZL], [/* Define if you have the __builtin_clzl builtin. */
+@%:@undef HAVE___BUILTIN_CLZL])
+m4trace:configure.ac:40: -1- AC_DEFINE_TRACE_LITERAL([HAVE___BUILTIN_CLZLL])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^HAVE___BUILTIN_CLZLL$])
+m4trace:configure.ac:40: -1- AH_OUTPUT([HAVE___BUILTIN_CLZLL], [/* Define if you have the __builtin_clzll builtin. */
+@%:@undef HAVE___BUILTIN_CLZLL])
+m4trace:configure.ac:43: -1- AC_DEFINE_TRACE_LITERAL([HAVE___BUILTIN_CTZ])
+m4trace:configure.ac:43: -1- m4_pattern_allow([^HAVE___BUILTIN_CTZ$])
+m4trace:configure.ac:43: -1- AH_OUTPUT([HAVE___BUILTIN_CTZ], [/* Define if you have the __builtin_ctz builtin. */
+@%:@undef HAVE___BUILTIN_CTZ])
+m4trace:configure.ac:46: -1- AC_DEFINE_TRACE_LITERAL([HAVE___BUILTIN_CTZL])
+m4trace:configure.ac:46: -1- m4_pattern_allow([^HAVE___BUILTIN_CTZL$])
+m4trace:configure.ac:46: -1- AH_OUTPUT([HAVE___BUILTIN_CTZL], [/* Define if you have the __builtin_ctzl builtin. */
+@%:@undef HAVE___BUILTIN_CTZL])
+m4trace:configure.ac:49: -1- AC_DEFINE_TRACE_LITERAL([HAVE___BUILTIN_CTZLL])
+m4trace:configure.ac:49: -1- m4_pattern_allow([^HAVE___BUILTIN_CTZLL$])
+m4trace:configure.ac:49: -1- AH_OUTPUT([HAVE___BUILTIN_CTZLL], [/* Define if you have the __builtin_ctzll builtin. */
+@%:@undef HAVE___BUILTIN_CTZLL])
+m4trace:configure.ac:52: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_SYNCHRONIZE])
+m4trace:configure.ac:52: -1- m4_pattern_allow([^HAVE___SYNC_SYNCHRONIZE$])
+m4trace:configure.ac:52: -1- AH_OUTPUT([HAVE___SYNC_SYNCHRONIZE], [/* Define if you have the __sync_synchronize builtin. */
+@%:@undef HAVE___SYNC_SYNCHRONIZE])
+m4trace:configure.ac:55: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_FETCH_AND_ADD])
+m4trace:configure.ac:55: -1- m4_pattern_allow([^HAVE___SYNC_FETCH_AND_ADD$])
+m4trace:configure.ac:55: -1- AH_OUTPUT([HAVE___SYNC_FETCH_AND_ADD], [/* Define if you have the __sync_fetch_and_add builtin. */
+@%:@undef HAVE___SYNC_FETCH_AND_ADD])
+m4trace:configure.ac:58: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_ADD_AND_FETCH])
+m4trace:configure.ac:58: -1- m4_pattern_allow([^HAVE___SYNC_ADD_AND_FETCH$])
+m4trace:configure.ac:58: -1- AH_OUTPUT([HAVE___SYNC_ADD_AND_FETCH], [/* Define if you have the __sync_add_and_fetch builtin. */
+@%:@undef HAVE___SYNC_ADD_AND_FETCH])
+m4trace:configure.ac:61: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_FETCH_AND_ADD_8])
+m4trace:configure.ac:61: -1- m4_pattern_allow([^HAVE___SYNC_FETCH_AND_ADD_8$])
+m4trace:configure.ac:61: -1- AH_OUTPUT([HAVE___SYNC_FETCH_AND_ADD_8], [/* Define if you have the __sync_fetch_and_add_8 builtin. */
+@%:@undef HAVE___SYNC_FETCH_AND_ADD_8])
+m4trace:configure.ac:65: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_ADD_AND_FETCH_8])
+m4trace:configure.ac:65: -1- m4_pattern_allow([^HAVE___SYNC_ADD_AND_FETCH_8$])
+m4trace:configure.ac:65: -1- AH_OUTPUT([HAVE___SYNC_ADD_AND_FETCH_8], [/* Define if you have the __sync_add_and_fetch_8 builtin. */
+@%:@undef HAVE___SYNC_ADD_AND_FETCH_8])
+m4trace:configure.ac:69: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_FETCH_AND_OR])
+m4trace:configure.ac:69: -1- m4_pattern_allow([^HAVE___SYNC_FETCH_AND_OR$])
+m4trace:configure.ac:69: -1- AH_OUTPUT([HAVE___SYNC_FETCH_AND_OR], [/* Define if you have the __sync_fetch_and_or builtin. */
+@%:@undef HAVE___SYNC_FETCH_AND_OR])
+m4trace:configure.ac:72: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_OR_AND_FETCH])
+m4trace:configure.ac:72: -1- m4_pattern_allow([^HAVE___SYNC_OR_AND_FETCH$])
+m4trace:configure.ac:72: -1- AH_OUTPUT([HAVE___SYNC_OR_AND_FETCH], [/* Define if you have the __sync_or_and_fetch builtin. */
+@%:@undef HAVE___SYNC_OR_AND_FETCH])
+m4trace:configure.ac:75: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_FETCH_AND_OR_8])
+m4trace:configure.ac:75: -1- m4_pattern_allow([^HAVE___SYNC_FETCH_AND_OR_8$])
+m4trace:configure.ac:75: -1- AH_OUTPUT([HAVE___SYNC_FETCH_AND_OR_8], [/* Define if you have the __sync_fetch_and_or_8 builtin. */
+@%:@undef HAVE___SYNC_FETCH_AND_OR_8])
+m4trace:configure.ac:79: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_OR_AND_FETCH_8])
+m4trace:configure.ac:79: -1- m4_pattern_allow([^HAVE___SYNC_OR_AND_FETCH_8$])
+m4trace:configure.ac:79: -1- AH_OUTPUT([HAVE___SYNC_OR_AND_FETCH_8], [/* Define if you have the __sync_or_and_fetch_8 builtin. */
+@%:@undef HAVE___SYNC_OR_AND_FETCH_8])
+m4trace:configure.ac:83: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_BOOL_COMPARE_AND_SWAP])
+m4trace:configure.ac:83: -1- m4_pattern_allow([^HAVE___SYNC_BOOL_COMPARE_AND_SWAP$])
+m4trace:configure.ac:83: -1- AH_OUTPUT([HAVE___SYNC_BOOL_COMPARE_AND_SWAP], [/* Define if you have the __sync_bool_compare_and_swap builtin. */
+@%:@undef HAVE___SYNC_BOOL_COMPARE_AND_SWAP])
+m4trace:configure.ac:86: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8])
+m4trace:configure.ac:86: -1- m4_pattern_allow([^HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8$])
+m4trace:configure.ac:86: -1- AH_OUTPUT([HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8], [/* Define if you have the __sync_bool_compare_and_swap_8 builtin. */
+@%:@undef HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8])
+m4trace:configure.ac:90: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_VAL_COMPARE_AND_SWAP])
+m4trace:configure.ac:90: -1- m4_pattern_allow([^HAVE___SYNC_VAL_COMPARE_AND_SWAP$])
+m4trace:configure.ac:90: -1- AH_OUTPUT([HAVE___SYNC_VAL_COMPARE_AND_SWAP], [/* Define if you have the __sync_val_compare_and_swap builtin. */
+@%:@undef HAVE___SYNC_VAL_COMPARE_AND_SWAP])
+m4trace:configure.ac:93: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_VAL_COMPARE_AND_SWAP_8])
+m4trace:configure.ac:93: -1- m4_pattern_allow([^HAVE___SYNC_VAL_COMPARE_AND_SWAP_8$])
+m4trace:configure.ac:93: -1- AH_OUTPUT([HAVE___SYNC_VAL_COMPARE_AND_SWAP_8], [/* Define if you have the __sync_val_compare_and_swap_8 builtin. */
+@%:@undef HAVE___SYNC_VAL_COMPARE_AND_SWAP_8])
+m4trace:configure.ac:97: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_LOCK_TEST_AND_SET])
+m4trace:configure.ac:97: -1- m4_pattern_allow([^HAVE___SYNC_LOCK_TEST_AND_SET$])
+m4trace:configure.ac:97: -1- AH_OUTPUT([HAVE___SYNC_LOCK_TEST_AND_SET], [/* Define if you have the __sync_lock_test_and_set builtin. */
+@%:@undef HAVE___SYNC_LOCK_TEST_AND_SET])
+m4trace:configure.ac:100: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_LOCK_TEST_AND_SET_VAL])
+m4trace:configure.ac:100: -1- m4_pattern_allow([^HAVE___SYNC_LOCK_TEST_AND_SET_VAL$])
+m4trace:configure.ac:100: -1- AH_OUTPUT([HAVE___SYNC_LOCK_TEST_AND_SET_VAL], [/* Define if you have the __sync_lock_test_and_set_val builtin. */
+@%:@undef HAVE___SYNC_LOCK_TEST_AND_SET_VAL])
+m4trace:configure.ac:103: -1- AC_DEFINE_TRACE_LITERAL([HAVE___SYNC_LOCK_RELEASE_SET])
+m4trace:configure.ac:103: -1- m4_pattern_allow([^HAVE___SYNC_LOCK_RELEASE_SET$])
+m4trace:configure.ac:103: -1- AH_OUTPUT([HAVE___SYNC_LOCK_RELEASE_SET], [/* Define if you have the __sync_lock_release_set builtin. */
+@%:@undef HAVE___SYNC_LOCK_RELEASE_SET])
+m4trace:configure.ac:121: -1- AC_DEFINE_TRACE_LITERAL([HAVE_CXX_AUTO])
+m4trace:configure.ac:121: -1- m4_pattern_allow([^HAVE_CXX_AUTO$])
+m4trace:configure.ac:121: -1- AH_OUTPUT([HAVE_CXX_AUTO], [/* Define if the C++ compiler understands \'auto\'. */
+@%:@undef HAVE_CXX_AUTO])
+m4trace:configure.ac:136: -1- AC_DEFINE_TRACE_LITERAL([HAVE_CXX_CONSTEXPR])
+m4trace:configure.ac:136: -1- m4_pattern_allow([^HAVE_CXX_CONSTEXPR$])
+m4trace:configure.ac:136: -1- AH_OUTPUT([HAVE_CXX_CONSTEXPR], [/* Define if the C++ compiler understands constexpr. */
+@%:@undef HAVE_CXX_CONSTEXPR])
+m4trace:configure.ac:143: -1- AC_DEFINE_TRACE_LITERAL([HAVE_CXX_STATIC_ASSERT])
+m4trace:configure.ac:143: -1- m4_pattern_allow([^HAVE_CXX_STATIC_ASSERT$])
+m4trace:configure.ac:143: -1- AH_OUTPUT([HAVE_CXX_STATIC_ASSERT], [/* Define if the C++ compiler understands static_assert. */
+@%:@undef HAVE_CXX_STATIC_ASSERT])
+m4trace:configure.ac:150: -1- AC_DEFINE_TRACE_LITERAL([HAVE_CXX_RVALUE_REFERENCES])
+m4trace:configure.ac:150: -1- m4_pattern_allow([^HAVE_CXX_RVALUE_REFERENCES$])
+m4trace:configure.ac:150: -1- AH_OUTPUT([HAVE_CXX_RVALUE_REFERENCES], [/* Define if the C++ compiler understands rvalue references. */
+@%:@undef HAVE_CXX_RVALUE_REFERENCES])
+m4trace:configure.ac:157: -1- AC_DEFINE_TRACE_LITERAL([HAVE_CXX_TEMPLATE_ALIAS])
+m4trace:configure.ac:157: -1- m4_pattern_allow([^HAVE_CXX_TEMPLATE_ALIAS$])
+m4trace:configure.ac:157: -1- AH_OUTPUT([HAVE_CXX_TEMPLATE_ALIAS], [/* Define if the C++ compiler understands template alias. */
+@%:@undef HAVE_CXX_TEMPLATE_ALIAS])
+m4trace:configure.ac:160: -1- AH_OUTPUT([HAVE_TYPE_TRAITS], [/* Define to 1 if you have the <type_traits> header file. */
+@%:@undef HAVE_TYPE_TRAITS])
+m4trace:configure.ac:160: -1- AC_DEFINE_TRACE_LITERAL([HAVE_TYPE_TRAITS])
+m4trace:configure.ac:160: -1- m4_pattern_allow([^HAVE_TYPE_TRAITS$])
+m4trace:configure.ac:168: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STD_HASH])
+m4trace:configure.ac:168: -1- m4_pattern_allow([^HAVE_STD_HASH$])
+m4trace:configure.ac:168: -1- AH_OUTPUT([HAVE_STD_HASH], [/* Define if you have std::hash. */
+@%:@undef HAVE_STD_HASH])
+m4trace:configure.ac:174: -1- AC_DEFINE_TRACE_LITERAL([HAVE___HAS_TRIVIAL_COPY])
+m4trace:configure.ac:174: -1- m4_pattern_allow([^HAVE___HAS_TRIVIAL_COPY$])
+m4trace:configure.ac:174: -1- AH_OUTPUT([HAVE___HAS_TRIVIAL_COPY], [/* Define if you have the __has_trivial_copy compiler intrinsic. */
+@%:@undef HAVE___HAS_TRIVIAL_COPY])
+m4trace:configure.ac:194: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STD_IS_TRIVIALLY_COPYABLE])
+m4trace:configure.ac:194: -1- m4_pattern_allow([^HAVE_STD_IS_TRIVIALLY_COPYABLE$])
+m4trace:configure.ac:194: -1- AH_OUTPUT([HAVE_STD_IS_TRIVIALLY_COPYABLE], [/* Define if you have the std::is_trivially_copyable template. */
+@%:@undef HAVE_STD_IS_TRIVIALLY_COPYABLE])
+m4trace:configure.ac:200: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STD_IS_RVALUE_REFERENCE])
+m4trace:configure.ac:200: -1- m4_pattern_allow([^HAVE_STD_IS_RVALUE_REFERENCE$])
+m4trace:configure.ac:200: -1- AH_OUTPUT([HAVE_STD_IS_RVALUE_REFERENCE], [/* Define if you have the std::is_rvalue_reference template. */
+@%:@undef HAVE_STD_IS_RVALUE_REFERENCE])
+m4trace:configure.ac:239: -1- AC_DEFINE_TRACE_LITERAL([HAVE_TCMALLOC])
+m4trace:configure.ac:239: -1- m4_pattern_allow([^HAVE_TCMALLOC$])
+m4trace:configure.ac:239: -1- AH_OUTPUT([HAVE_TCMALLOC], [/* Define if you are using libtcmalloc for malloc. */
+@%:@undef HAVE_TCMALLOC])
+m4trace:configure.ac:243: -1- AC_DEFINE_TRACE_LITERAL([HAVE_JEMALLOC])
+m4trace:configure.ac:243: -1- m4_pattern_allow([^HAVE_JEMALLOC$])
+m4trace:configure.ac:243: -1- AH_OUTPUT([HAVE_JEMALLOC], [/* Define if you are using libjemalloc for malloc. */
+@%:@undef HAVE_JEMALLOC])
+m4trace:configure.ac:247: -1- AC_DEFINE_TRACE_LITERAL([HAVE_FLOW_MALLOC])
+m4trace:configure.ac:247: -1- m4_pattern_allow([^HAVE_FLOW_MALLOC$])
+m4trace:configure.ac:247: -1- AH_OUTPUT([HAVE_FLOW_MALLOC], [/* Define if you are using libflow for malloc. */
+@%:@undef HAVE_FLOW_MALLOC])
+m4trace:configure.ac:251: -1- AC_DEFINE_TRACE_LITERAL([HAVE_HOARD_MALLOC])
+m4trace:configure.ac:251: -1- m4_pattern_allow([^HAVE_HOARD_MALLOC$])
+m4trace:configure.ac:251: -1- AH_OUTPUT([HAVE_HOARD_MALLOC], [/* Define if you are using libhoard for malloc. */
+@%:@undef HAVE_HOARD_MALLOC])
+m4trace:configure.ac:257: -1- AC_SUBST([MALLOC_LIBS])
+m4trace:configure.ac:257: -1- AC_SUBST_TRACE([MALLOC_LIBS])
+m4trace:configure.ac:257: -1- m4_pattern_allow([^MALLOC_LIBS$])
+m4trace:configure.ac:274: -1- AC_DEFINE_TRACE_LITERAL([HAVE_OFF_T_IS_LONG])
+m4trace:configure.ac:274: -1- m4_pattern_allow([^HAVE_OFF_T_IS_LONG$])
+m4trace:configure.ac:274: -1- AH_OUTPUT([HAVE_OFF_T_IS_LONG], [/* Define if off_t and long are the same type. */
+@%:@undef HAVE_OFF_T_IS_LONG])
+m4trace:configure.ac:275: -1- AC_DEFINE_TRACE_LITERAL([HAVE_OFF_T_IS_LONG_LONG])
+m4trace:configure.ac:275: -1- m4_pattern_allow([^HAVE_OFF_T_IS_LONG_LONG$])
+m4trace:configure.ac:275: -1- AH_OUTPUT([HAVE_OFF_T_IS_LONG_LONG], [/* Define if off_t and long long are the same type. */
+@%:@undef HAVE_OFF_T_IS_LONG_LONG])
+m4trace:configure.ac:276: -1- AC_DEFINE_TRACE_LITERAL([HAVE_INT64_T_IS_LONG])
+m4trace:configure.ac:276: -1- m4_pattern_allow([^HAVE_INT64_T_IS_LONG$])
+m4trace:configure.ac:276: -1- AH_OUTPUT([HAVE_INT64_T_IS_LONG], [/* Define if int64_t and long are the same type. */
+@%:@undef HAVE_INT64_T_IS_LONG])
+m4trace:configure.ac:277: -1- AC_DEFINE_TRACE_LITERAL([HAVE_INT64_T_IS_LONG_LONG])
+m4trace:configure.ac:277: -1- m4_pattern_allow([^HAVE_INT64_T_IS_LONG_LONG$])
+m4trace:configure.ac:277: -1- AH_OUTPUT([HAVE_INT64_T_IS_LONG_LONG], [/* Define if int64_t and long long are the same type. */
+@%:@undef HAVE_INT64_T_IS_LONG_LONG])
+m4trace:configure.ac:278: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SIZE_T_IS_UNSIGNED])
+m4trace:configure.ac:278: -1- m4_pattern_allow([^HAVE_SIZE_T_IS_UNSIGNED$])
+m4trace:configure.ac:278: -1- AH_OUTPUT([HAVE_SIZE_T_IS_UNSIGNED], [/* Define if size_t and unsigned are the same type. */
+@%:@undef HAVE_SIZE_T_IS_UNSIGNED])
+m4trace:configure.ac:279: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SIZE_T_IS_UNSIGNED_LONG])
+m4trace:configure.ac:279: -1- m4_pattern_allow([^HAVE_SIZE_T_IS_UNSIGNED_LONG$])
+m4trace:configure.ac:279: -1- AH_OUTPUT([HAVE_SIZE_T_IS_UNSIGNED_LONG], [/* Define if size_t and unsigned long are the same type. */
+@%:@undef HAVE_SIZE_T_IS_UNSIGNED_LONG])
+m4trace:configure.ac:280: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG])
+m4trace:configure.ac:280: -1- m4_pattern_allow([^HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG$])
+m4trace:configure.ac:280: -1- AH_OUTPUT([HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG], [/* Define if size_t and unsigned long long are the same type. */
+@%:@undef HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG])
+m4trace:configure.ac:282: -1- AC_DEFINE_TRACE_LITERAL([HAVE_LONG_LONG])
+m4trace:configure.ac:282: -1- m4_pattern_allow([^HAVE_LONG_LONG$])
+m4trace:configure.ac:282: -1- AH_OUTPUT([HAVE_LONG_LONG], [/* Define to 1 if the system has the type `long long\'. */
+@%:@undef HAVE_LONG_LONG])
+m4trace:configure.ac:283: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_SHORT])
+m4trace:configure.ac:283: -1- m4_pattern_allow([^SIZEOF_SHORT$])
+m4trace:configure.ac:283: -1- AH_OUTPUT([SIZEOF_SHORT], [/* The size of `short\', as computed by sizeof. */
+@%:@undef SIZEOF_SHORT])
+m4trace:configure.ac:284: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_INT])
+m4trace:configure.ac:284: -1- m4_pattern_allow([^SIZEOF_INT$])
+m4trace:configure.ac:284: -1- AH_OUTPUT([SIZEOF_INT], [/* The size of `int\', as computed by sizeof. */
+@%:@undef SIZEOF_INT])
+m4trace:configure.ac:285: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_LONG])
+m4trace:configure.ac:285: -1- m4_pattern_allow([^SIZEOF_LONG$])
+m4trace:configure.ac:285: -1- AH_OUTPUT([SIZEOF_LONG], [/* The size of `long\', as computed by sizeof. */
+@%:@undef SIZEOF_LONG])
+m4trace:configure.ac:286: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_LONG_LONG])
+m4trace:configure.ac:286: -1- m4_pattern_allow([^SIZEOF_LONG_LONG$])
+m4trace:configure.ac:286: -1- AH_OUTPUT([SIZEOF_LONG_LONG], [/* The size of `long long\', as computed by sizeof. */
+@%:@undef SIZEOF_LONG_LONG])
+m4trace:configure.ac:287: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_VOID_P])
+m4trace:configure.ac:287: -1- m4_pattern_allow([^SIZEOF_VOID_P$])
+m4trace:configure.ac:287: -1- AH_OUTPUT([SIZEOF_VOID_P], [/* The size of `void *\', as computed by sizeof. */
+@%:@undef SIZEOF_VOID_P])
+m4trace:configure.ac:289: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_GETLINE])
+m4trace:configure.ac:289: -1- m4_pattern_allow([^HAVE_DECL_GETLINE$])
+m4trace:configure.ac:289: -1- AH_OUTPUT([HAVE_DECL_GETLINE], [/* Define to 1 if you have the declaration of `getline\', and to 0 if you
+   don\'t. */
+@%:@undef HAVE_DECL_GETLINE])
+m4trace:configure.ac:291: -1- AH_OUTPUT([HAVE_TIME_H], [/* Define to 1 if you have the <time.h> header file. */
+@%:@undef HAVE_TIME_H])
+m4trace:configure.ac:291: -1- AH_OUTPUT([HAVE_EXECINFO_H], [/* Define to 1 if you have the <execinfo.h> header file. */
+@%:@undef HAVE_EXECINFO_H])
+m4trace:configure.ac:292: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_CLOCK_GETTIME])
+m4trace:configure.ac:292: -1- m4_pattern_allow([^HAVE_DECL_CLOCK_GETTIME$])
+m4trace:configure.ac:292: -1- AH_OUTPUT([HAVE_DECL_CLOCK_GETTIME], [/* Define to 1 if you have the declaration of `clock_gettime\', and to 0 if you
+   don\'t. */
+@%:@undef HAVE_DECL_CLOCK_GETTIME])
+m4trace:configure.ac:296: -1- AH_OUTPUT([HAVE_CLOCK_GETTIME], [/* Define to 1 if you have the `clock_gettime\' function. */
+@%:@undef HAVE_CLOCK_GETTIME])
+m4trace:configure.ac:296: -1- AC_DEFINE_TRACE_LITERAL([HAVE_CLOCK_GETTIME])
+m4trace:configure.ac:296: -1- m4_pattern_allow([^HAVE_CLOCK_GETTIME$])
+m4trace:configure.ac:304: -1- AC_DEFINE_TRACE_LITERAL([MASSTREE_ROW_TYPE_ARRAY])
+m4trace:configure.ac:304: -1- m4_pattern_allow([^MASSTREE_ROW_TYPE_ARRAY$])
+m4trace:configure.ac:304: -1- AH_OUTPUT([MASSTREE_ROW_TYPE_ARRAY], [/* Define if the default row type is value_timed_array. */
+@%:@undef MASSTREE_ROW_TYPE_ARRAY])
+m4trace:configure.ac:306: -1- AC_DEFINE_TRACE_LITERAL([MASSTREE_ROW_TYPE_ARRAY_VER])
+m4trace:configure.ac:306: -1- m4_pattern_allow([^MASSTREE_ROW_TYPE_ARRAY_VER$])
+m4trace:configure.ac:306: -1- AH_OUTPUT([MASSTREE_ROW_TYPE_ARRAY_VER], [/* Define if the default row type is value_timed_array_ver. */
+@%:@undef MASSTREE_ROW_TYPE_ARRAY_VER])
+m4trace:configure.ac:308: -1- AC_DEFINE_TRACE_LITERAL([MASSTREE_ROW_TYPE_BAG])
+m4trace:configure.ac:308: -1- m4_pattern_allow([^MASSTREE_ROW_TYPE_BAG$])
+m4trace:configure.ac:308: -1- AH_OUTPUT([MASSTREE_ROW_TYPE_BAG], [/* Define if the default row type is value_timed_bag. */
+@%:@undef MASSTREE_ROW_TYPE_BAG])
+m4trace:configure.ac:310: -1- AC_DEFINE_TRACE_LITERAL([MASSTREE_ROW_TYPE_STR])
+m4trace:configure.ac:310: -1- m4_pattern_allow([^MASSTREE_ROW_TYPE_STR$])
+m4trace:configure.ac:310: -1- AH_OUTPUT([MASSTREE_ROW_TYPE_STR], [/* Define if the default row type is value_timed_str. */
+@%:@undef MASSTREE_ROW_TYPE_STR])
+m4trace:configure.ac:319: -1- AC_DEFINE_TRACE_LITERAL([MASSTREE_MAXKEYLEN])
+m4trace:configure.ac:319: -1- m4_pattern_allow([^MASSTREE_MAXKEYLEN$])
+m4trace:configure.ac:319: -1- AH_OUTPUT([MASSTREE_MAXKEYLEN], [/* Maximum key length */
+@%:@undef MASSTREE_MAXKEYLEN])
+m4trace:configure.ac:329: -1- AC_DEFINE_TRACE_LITERAL([HAVE_MADV_HUGEPAGE])
+m4trace:configure.ac:329: -1- m4_pattern_allow([^HAVE_MADV_HUGEPAGE$])
+m4trace:configure.ac:329: -1- AH_OUTPUT([HAVE_MADV_HUGEPAGE], [/* Define if MADV_HUGEPAGE is supported. */
+@%:@undef HAVE_MADV_HUGEPAGE])
+m4trace:configure.ac:340: -1- AC_DEFINE_TRACE_LITERAL([HAVE_MAP_HUGETLB])
+m4trace:configure.ac:340: -1- m4_pattern_allow([^HAVE_MAP_HUGETLB$])
+m4trace:configure.ac:340: -1- AH_OUTPUT([HAVE_MAP_HUGETLB], [/* Define if MAP_HUGETLB is supported. */
+@%:@undef HAVE_MAP_HUGETLB])
+m4trace:configure.ac:353: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SUPERPAGE])
+m4trace:configure.ac:353: -1- m4_pattern_allow([^HAVE_SUPERPAGE$])
+m4trace:configure.ac:353: -1- AH_OUTPUT([HAVE_SUPERPAGE], [/* Define if superpage support is enabled. */
+@%:@undef HAVE_SUPERPAGE])
+m4trace:configure.ac:360: -1- AC_DEFINE_TRACE_LITERAL([HAVE_MEMDEBUG])
+m4trace:configure.ac:360: -1- m4_pattern_allow([^HAVE_MEMDEBUG$])
+m4trace:configure.ac:360: -1- AH_OUTPUT([HAVE_MEMDEBUG], [/* Define if memory debugging support is enabled. */
+@%:@undef HAVE_MEMDEBUG])
+m4trace:configure.ac:370: -1- AC_DEFINE_TRACE_LITERAL([ENABLE_ASSERTIONS])
+m4trace:configure.ac:370: -1- m4_pattern_allow([^ENABLE_ASSERTIONS$])
+m4trace:configure.ac:370: -1- AH_OUTPUT([ENABLE_ASSERTIONS], [/* Define to enable debugging assertions. */
+@%:@undef ENABLE_ASSERTIONS])
+m4trace:configure.ac:377: -1- AC_DEFINE_TRACE_LITERAL([ENABLE_PRECONDITIONS])
+m4trace:configure.ac:377: -1- m4_pattern_allow([^ENABLE_PRECONDITIONS$])
+m4trace:configure.ac:377: -1- AH_OUTPUT([ENABLE_PRECONDITIONS], [/* Define to enable precondition assertions. */
+@%:@undef ENABLE_PRECONDITIONS])
+m4trace:configure.ac:379: -1- AC_DEFINE_TRACE_LITERAL([ENABLE_PRECONDITIONS])
+m4trace:configure.ac:379: -1- m4_pattern_allow([^ENABLE_PRECONDITIONS$])
+m4trace:configure.ac:379: -1- AH_OUTPUT([ENABLE_PRECONDITIONS], [/* Define to enable precondition assertions. */
+@%:@undef ENABLE_PRECONDITIONS])
+m4trace:configure.ac:386: -1- AC_DEFINE_TRACE_LITERAL([ENABLE_INVARIANTS])
+m4trace:configure.ac:386: -1- m4_pattern_allow([^ENABLE_INVARIANTS$])
+m4trace:configure.ac:386: -1- AH_OUTPUT([ENABLE_INVARIANTS], [/* Define to enable invariant assertions. */
+@%:@undef ENABLE_INVARIANTS])
+m4trace:configure.ac:388: -1- AC_DEFINE_TRACE_LITERAL([ENABLE_INVARIANTS])
+m4trace:configure.ac:388: -1- m4_pattern_allow([^ENABLE_INVARIANTS$])
+m4trace:configure.ac:388: -1- AH_OUTPUT([ENABLE_INVARIANTS], [/* Define to enable invariant assertions. */
+@%:@undef ENABLE_INVARIANTS])
+m4trace:configure.ac:391: -1- AC_DEFINE_TRACE_LITERAL([CACHE_LINE_SIZE])
+m4trace:configure.ac:391: -1- m4_pattern_allow([^CACHE_LINE_SIZE$])
+m4trace:configure.ac:391: -1- AH_OUTPUT([CACHE_LINE_SIZE], [/* Assumed size of a cache line. */
+@%:@undef CACHE_LINE_SIZE])
+m4trace:configure.ac:393: -1- AH_OUTPUT([00001], [#ifndef MASSTREE_CONFIG_H_INCLUDED
+#define MASSTREE_CONFIG_H_INCLUDED 1])
+m4trace:configure.ac:396: -1- AH_OUTPUT([zzzz2], [#if !FORCE_ENABLE_ASSERTIONS && !ENABLE_ASSERTIONS
+# define NDEBUG 1
+#endif
+
+/** @brief Assert macro that always runs. */
+extern void fail_always_assert(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#define always_assert(x, ...) do { if (!(x)) fail_always_assert(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#define mandatory_assert always_assert
+
+/** @brief Assert macro for invariants.
+
+    masstree_invariant(x) is executed if --enable-invariants or
+    --enable-assertions. */
+extern void fail_masstree_invariant(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_INVARIANTS) && ENABLE_ASSERTIONS) || ENABLE_INVARIANTS
+#define masstree_invariant(x, ...) do { if (!(x)) fail_masstree_invariant(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#else
+#define masstree_invariant(x, ...) do { } while (0)
+#endif
+
+/** @brief Assert macro for preconditions.
+
+    masstree_precondition(x) is executed if --enable-preconditions or
+    --enable-assertions. */
+extern void fail_masstree_precondition(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_PRECONDITIONS) && ENABLE_ASSERTIONS) || ENABLE_PRECONDITIONS
+#define masstree_precondition(x, ...) do { if (!(x)) fail_masstree_precondition(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#else
+#define masstree_precondition(x, ...) do { } while (0)
+#endif
+
+#ifndef invariant
+#define invariant masstree_invariant
+#endif
+#ifndef precondition
+#define precondition masstree_precondition
+#endif
+
+#endif])
+m4trace:configure.ac:436: -1- AC_DEFINE_TRACE_LITERAL([HAVE_UNALIGNED_ACCESS])
+m4trace:configure.ac:436: -1- m4_pattern_allow([^HAVE_UNALIGNED_ACCESS$])
+m4trace:configure.ac:436: -1- AH_OUTPUT([HAVE_UNALIGNED_ACCESS], [/* Define if unaligned accesses are OK. */
+@%:@undef HAVE_UNALIGNED_ACCESS])
+m4trace:configure.ac:438: -1- AC_SUBST([LIB@&t@OBJS], [$ac_libobjs])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([LIB@&t@OBJS])
+m4trace:configure.ac:438: -1- m4_pattern_allow([^LIB@&t@OBJS$])
+m4trace:configure.ac:438: -1- AC_SUBST([LTLIBOBJS], [$ac_ltlibobjs])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([LTLIBOBJS])
+m4trace:configure.ac:438: -1- m4_pattern_allow([^LTLIBOBJS$])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([top_builddir])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([top_build_prefix])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([srcdir])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([abs_srcdir])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([top_srcdir])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([abs_top_srcdir])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([builddir])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([abs_builddir])
+m4trace:configure.ac:438: -1- AC_SUBST_TRACE([abs_top_builddir])
diff --git a/silo/masstree/btree_leaflink.hh b/silo/masstree/btree_leaflink.hh
new file mode 100644 (file)
index 0000000..d1bb060
--- /dev/null
@@ -0,0 +1,124 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef BTREE_LEAFLINK_HH
+#define BTREE_LEAFLINK_HH 1
+#include "compiler.hh"
+
+/** @brief Operations to manage linked lists of B+tree leaves.
+
+    N is the type of nodes. CONCURRENT is true to make operations
+    concurrency-safe (e.g. compare-and-swaps, fences), false to leave them
+    unsafe (only OK on single threaded code, but faster). */
+template <typename N, bool CONCURRENT = N::concurrent> struct btree_leaflink {};
+
+
+// This is the normal version of btree_leaflink; it uses lock-free linked list
+// operations.
+template <typename N> struct btree_leaflink<N, true> {
+  private:
+    static inline N *mark(N *n) {
+        return reinterpret_cast<N *>(reinterpret_cast<uintptr_t>(n) + 1);
+    }
+    static inline bool is_marked(N *n) {
+        return reinterpret_cast<uintptr_t>(n) & 1;
+    }
+    template <typename SF>
+    static inline N *lock_next(N *n, SF spin_function) {
+        while (1) {
+            N *next = n->next_.ptr;
+            if (!next
+                || (!is_marked(next)
+                    && bool_cmpxchg(&n->next_.ptr, next, mark(next))))
+                return next;
+            spin_function();
+        }
+    }
+
+  public:
+    /** @brief Insert a new node @a nr at the right of node @a n.
+        @pre @a n is locked.
+
+        Concurrency correctness: Ensures that all "next" pointers are always
+        valid, even if @a n's successor is deleted concurrently. */
+    static void link_split(N *n, N *nr) {
+        link_split(n, nr, relax_fence_function());
+    }
+    /** @overload */
+    template <typename SF>
+    static void link_split(N *n, N *nr, SF spin_function) {
+        nr->prev_ = n;
+        N *next = lock_next(n, spin_function);
+        nr->next_.ptr = next;
+        if (next)
+            next->prev_ = nr;
+        fence();
+        n->next_.ptr = nr;
+    }
+
+    /** @brief Unlink @a n from the list.
+        @pre @a n is locked.
+
+        Concurrency correctness: Works even in the presence of concurrent
+        splits and deletes. */
+    static void unlink(N *n) {
+        unlink(n, relax_fence_function());
+    }
+    /** @overload */
+    template <typename SF>
+    static void unlink(N *n, SF spin_function) {
+        // Assume node order A <-> N <-> B. Since n is locked, n cannot split;
+        // next node will always be B or one of its successors.
+        N *next = lock_next(n, spin_function);
+        N *prev;
+        while (1) {
+            prev = n->prev_;
+            if (bool_cmpxchg(&prev->next_.ptr, n, mark(n)))
+                break;
+            spin_function();
+        }
+        if (next)
+            next->prev_ = prev;
+        fence();
+        prev->next_.ptr = next;
+    }
+};
+
+
+// This is the single-threaded-only fast version of btree_leaflink.
+template <typename N> struct btree_leaflink<N, false> {
+    static void link_split(N *n, N *nr) {
+        link_split(n, nr, do_nothing());
+    }
+    template <typename SF>
+    static void link_split(N *n, N *nr, SF) {
+        nr->prev_ = n;
+        nr->next_.ptr = n->next_.ptr;
+        n->next_.ptr = nr;
+        if (nr->next_.ptr)
+            nr->next_.ptr->prev_ = nr;
+    }
+    static void unlink(N *n) {
+        unlink(n, do_nothing());
+    }
+    template <typename SF>
+    static void unlink(N *n, SF) {
+        if (n->next_.ptr)
+            n->next_.ptr->prev_ = n->prev_;
+        n->prev_->next_.ptr = n->next_.ptr;
+    }
+};
+
+#endif
diff --git a/silo/masstree/checkpoint.cc b/silo/masstree/checkpoint.cc
new file mode 100644 (file)
index 0000000..0b43a88
--- /dev/null
@@ -0,0 +1,30 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "checkpoint.hh"
+
+// add one key/value to a checkpoint.
+// called by checkpoint_tree() for each node.
+bool ckstate::visit_value(Str key, const row_type* value, threadinfo&) {
+    if (endkey && key >= endkey)
+        return false;
+    if (!row_is_marker(value)) {
+        msgpack::unparser<kvout> up(*vals);
+        up.write(key).write_wide(value->timestamp());
+        value->checkpoint_write(up);
+        ++count;
+    }
+    return true;
+}
diff --git a/silo/masstree/checkpoint.hh b/silo/masstree/checkpoint.hh
new file mode 100644 (file)
index 0000000..c6914a0
--- /dev/null
@@ -0,0 +1,56 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_CHECKPOINT_HH
+#define MASSTREE_CHECKPOINT_HH
+#include "kvrow.hh"
+#include "kvio.hh"
+#include "msgpack.hh"
+
+struct ckstate {
+    kvout *vals; // key, val, timestamp in msgpack
+    uint64_t count; // total nodes written
+    uint64_t bytes;
+    pthread_cond_t state_cond;
+    volatile int state;
+    threadinfo *ti;
+    Str startkey;
+    Str endkey;
+
+    template <typename SS, typename K>
+    void visit_leaf(const SS&, const K&, threadinfo&) {
+    }
+    bool visit_value(Str key, const row_type* value, threadinfo& ti);
+
+    template <typename T>
+    static void insert(T& table, msgpack::parser& par, threadinfo& ti);
+};
+
+template <typename T>
+void ckstate::insert(T& table, msgpack::parser& par, threadinfo& ti) {
+    Str key;
+    kvtimestamp_t ts{};
+    par >> key >> ts;
+    row_type* row = row_type::checkpoint_read(par, ts, ti);
+
+    typename T::cursor_type lp(table, key);
+    bool found = lp.find_insert(ti);
+    masstree_invariant(!found); (void) found;
+    ti.advance_timestamp(lp.node_timestamp());
+    lp.value() = row;
+    lp.finish(1, ti);
+}
+
+#endif
diff --git a/silo/masstree/circular_int.hh b/silo/masstree/circular_int.hh
new file mode 100644 (file)
index 0000000..8acaa80
--- /dev/null
@@ -0,0 +1,146 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVDB_CIRCULAR_INT_HH
+#define KVDB_CIRCULAR_INT_HH 1
+#include "compiler.hh"
+
+template <typename T>
+class circular_int {
+  public:
+    typedef typename mass::make_unsigned<T>::type value_type;
+    typedef typename mass::make_signed<T>::type difference_type;
+
+    circular_int()
+        : v_() {
+    }
+    circular_int(T x)
+        : v_(x) {
+    }
+
+    value_type value() const {
+        return v_;
+    }
+
+    circular_int<T> &operator++() {
+        ++v_;
+        return *this;
+    }
+    circular_int<T> operator++(int) {
+        ++v_;
+        return circular_int<T>(v_ - 1);
+    }
+    circular_int<T> &operator--() {
+        --v_;
+        return *this;
+    }
+    circular_int<T> operator--(int) {
+        --v_;
+        return circular_int<T>(v_ + 1);
+    }
+    circular_int<T> &operator+=(unsigned x) {
+        v_ += x;
+        return *this;
+    }
+    circular_int<T> &operator+=(int x) {
+        v_ += x;
+        return *this;
+    }
+    circular_int<T> &operator-=(unsigned x) {
+        v_ -= x;
+        return *this;
+    }
+    circular_int<T> &operator-=(int x) {
+        v_ -= x;
+        return *this;
+    }
+
+    circular_int<T> cmpxchg(circular_int<T> expected, circular_int<T> desired) {
+        return ::cmpxchg(&v_, expected.v_, desired.v_);
+    }
+    circular_int<T> cmpxchg(T expected, T desired) {
+        return ::cmpxchg(&v_, expected, desired);
+    }
+
+    typedef value_type (circular_int<T>::*unspecified_bool_type)() const;
+    operator unspecified_bool_type() const {
+        return v_ != 0 ? &circular_int<T>::value : 0;
+    }
+    bool operator!() const {
+        return v_ == 0;
+    }
+
+    circular_int<T> operator+(unsigned x) const {
+        return circular_int<T>(v_ + x);
+    }
+    circular_int<T> operator+(int x) const {
+        return circular_int<T>(v_ + x);
+    }
+    circular_int<T> next_nonzero() const {
+        value_type v = v_ + 1;
+        return circular_int<T>(v + !v);
+    }
+    static value_type next_nonzero(value_type x) {
+        ++x;
+        return x + !x;
+    }
+    circular_int<T> operator-(unsigned x) const {
+        return circular_int<T>(v_ - x);
+    }
+    circular_int<T> operator-(int x) const {
+        return circular_int<T>(v_ - x);
+    }
+    difference_type operator-(circular_int<T> x) const {
+        return v_ - x.v_;
+    }
+
+    bool operator==(circular_int<T> x) const {
+        return v_ == x.v_;
+    }
+    bool operator!=(circular_int<T> x) const {
+        return !(*this == x);
+    }
+    static bool less(value_type a, value_type b) {
+        return difference_type(a - b) < 0;
+    }
+    static bool less_equal(value_type a, value_type b) {
+        return difference_type(a - b) <= 0;
+    }
+    bool operator<(circular_int<T> x) const {
+        return less(v_, x.v_);
+    }
+    bool operator<=(circular_int<T> x) const {
+        return !less(x.v_, v_);
+    }
+    bool operator>=(circular_int<T> x) const {
+        return !less(v_, x.v_);
+    }
+    bool operator>(circular_int<T> x) const {
+        return less(x.v_, v_);
+    }
+
+  private:
+    value_type v_;
+};
+
+typedef circular_int<uint64_t> kvepoch_t;
+
+template <typename T>
+inline circular_int<T> cmpxchg(circular_int<T> *object, circular_int<T> expected,
+                               circular_int<T> desired) {
+    return object->cmpxchg(expected, desired);
+}
+
+#endif
diff --git a/silo/masstree/clp.c b/silo/masstree/clp.c
new file mode 100644 (file)
index 0000000..cb9e5d8
--- /dev/null
@@ -0,0 +1,2506 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+/* clp.c - Complete source code for CLP.
+ * This file is part of CLP, the command line parser package.
+ *
+ * Copyright (c) 1997-2014 Eddie Kohler, ekohler@gmail.com
+ *
+ * CLP is free software. It is distributed under the GNU General Public
+ * License, Version 2, or, alternatively and at your discretion, under the
+ * more permissive (BSD-like) Click LICENSE file as described below.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, subject to the
+ * conditions listed in the Click LICENSE file, which is available in full at
+ * http://github.com/kohler/click/blob/master/LICENSE. The conditions
+ * include: you must preserve this copyright notice, and you cannot mention
+ * the copyright holders in advertising related to the Software without
+ * their permission. The Software is provided WITHOUT ANY WARRANTY, EXPRESS
+ * OR IMPLIED. This notice is a summary of the Click LICENSE file; the
+ * license in that file is binding. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "clp.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <ctype.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+/* By default, assume we have inttypes.h, strtoul, and uintptr_t. */
+#if !defined(HAVE_STRTOUL) && !defined(HAVE_CONFIG_H)
+# define HAVE_STRTOUL 1
+#endif
+#if defined(HAVE_INTTYPES_H) || !defined(HAVE_CONFIG_H)
+# include <inttypes.h>
+#endif
+#if !defined(HAVE_UINTPTR_T) && defined(HAVE_CONFIG_H)
+typedef unsigned long uintptr_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** @file clp.h
+ * @brief Functions for parsing command line options.
+ *
+ * The CLP functions are used to parse command line arugments into options.
+ * It automatically handles value parsing, error messages, long options with
+ * minimum prefix matching, short options, and negated options.
+ *
+ * The CLP model works like this.
+ *
+ * <ol>
+ * <li>The user declares an array of Clp_Option structures that define the
+ * options their program accepts.</li>
+ * <li>The user creates a Clp_Parser object using Clp_NewParser(), passing in
+ * the command line arguments to parse and the Clp_Option structures.</li>
+ * <li>A loop repeatedly calls Clp_Next() to parse the arguments.</li>
+ * </ol>
+ *
+ * Unlike many command line parsing libraries, CLP steps through all arguments
+ * one at a time, rather than slurping up all options at once.  This makes it
+ * meaningful to give an option more than once.
+ *
+ * Here's an example.
+ *
+ * @code
+ * #define ANIMAL_OPT 1
+ * #define VEGETABLE_OPT 2
+ * #define MINERALS_OPT 3
+ * #define USAGE_OPT 4
+ *
+ * static const Clp_Option options[] = {
+ *     { "animal", 'a', ANIMAL_OPT, Clp_ValString, 0 },
+ *     { "vegetable", 'v', VEGETABLE_OPT, Clp_ValString, Clp_Negate | Clp_Optional },
+ *     { "minerals", 'm', MINERALS_OPT, Clp_ValInt, 0 },
+ *     { "usage", 0, USAGE_OPT, 0, 0 }
+ * };
+ *
+ * int main(int argc, char *argv[]) {
+ *     Clp_Parser *clp = Clp_NewParser(argc, argv,
+ *               sizeof(options) / sizeof(options[0]), options);
+ *     int opt;
+ *     while ((opt = Clp_Next(clp)) != Clp_Done)
+ *         switch (opt) {
+ *         case ANIMAL_OPT:
+ *             fprintf(stderr, "animal is %s\n", clp->val.s);
+ *             break;
+ *         case VEGETABLE_OPT:
+ *             if (clp->negated)
+ *                 fprintf(stderr, "no vegetables!\n");
+ *             else if (clp->have_val)
+ *                 fprintf(stderr, "vegetable is %s\n", clp->val.s);
+ *             else
+ *                 fprintf(stderr, "vegetables OK\n");
+ *             break;
+ *         case MINERALS_OPT:
+ *             fprintf(stderr, "%d minerals\n", clp->val.i);
+ *             break;
+ *         case USAGE_OPT:
+ *             fprintf(stderr, "Usage: 20q [--animal=ANIMAL] [--vegetable[=VEGETABLE]] [--minerals=N]\n");
+ *             break;
+ *         case Clp_NotOption:
+ *             fprintf(stderr, "non-option %s\n", clp->vstr);
+ *             break;
+ *         }
+ *     }
+ * }
+ * @endcode
+ *
+ * Here are a couple of executions.
+ *
+ * <pre>
+ * % ./20q --animal=cat
+ * animal is cat
+ * % ./20q --animal=cat -a dog -afish --animal bird --an=snake
+ * animal is cat
+ * animal is dog
+ * animal is fish
+ * animal is bird
+ * animal is snake
+ * % ./20q --no-vegetables
+ * no vegetables!
+ * % ./20q -v
+ * vegetables OK
+ * % ./20q -vkale
+ * vegetable is kale
+ * % ./20q -m10
+ * 10 minerals
+ * % ./20q -m foo
+ * '-m' expects an integer, not 'foo'
+ * </pre>
+ */
+
+
+/* Option types for Clp_SetOptionChar */
+#define Clp_DoubledLong                (Clp_LongImplicit * 2)
+
+#define Clp_InitialValType     8
+#define MAX_AMBIGUOUS_VALUES   4
+
+typedef struct {
+    int val_type;
+    Clp_ValParseFunc func;
+    int flags;
+    void *user_data;
+} Clp_ValType;
+
+typedef struct {
+    unsigned ilong : 1;
+    unsigned ishort : 1;
+    unsigned imandatory : 1;
+    unsigned ioptional : 1;
+    unsigned ipos : 1;
+    unsigned ineg : 1;
+    unsigned iprefmatch : 1;
+    unsigned lmmpos_short : 1;
+    unsigned lmmneg_short : 1;
+    unsigned char ilongoff;
+    int lmmpos;
+    int lmmneg;
+} Clp_InternOption;
+
+
+#define Clp_OptionCharsSize    5
+
+typedef struct {
+    int c;
+    int type;
+} Clp_Oclass;
+#define Clp_OclassSize         10
+
+typedef struct Clp_Internal {
+    const Clp_Option *opt;
+    Clp_InternOption *iopt;
+    int nopt;
+    unsigned opt_generation;
+
+    Clp_ValType *valtype;
+    int nvaltype;
+
+    const char * const *argv;
+    int argc;
+
+    Clp_Oclass oclass[Clp_OclassSize];
+    int noclass;
+    int long1pos;
+    int long1neg;
+    int utf8;
+
+    char option_chars[Clp_OptionCharsSize];
+    const char *xtext;
+
+    const char *program_name;
+    void (*error_handler)(Clp_Parser *, const char *);
+
+    int option_processing;
+    int current_option;
+
+    unsigned char is_short;
+    unsigned char whole_negated; /* true if negated by an option character */
+    unsigned char could_be_short;
+    unsigned char current_short;
+    unsigned char negated_by_no;
+
+    int ambiguous;
+    int ambiguous_values[MAX_AMBIGUOUS_VALUES];
+} Clp_Internal;
+
+
+struct Clp_ParserState {
+    const char * const *argv;
+    int argc;
+
+    char option_chars[Clp_OptionCharsSize];
+    const char *xtext;
+
+    int option_processing;
+
+    unsigned opt_generation;
+    int current_option;
+    unsigned char is_short;
+    unsigned char whole_negated;
+    unsigned char current_short;
+    unsigned char negated_by_no;
+};
+
+
+typedef struct Clp_StringList {
+    Clp_Option *items;
+    Clp_InternOption *iopt;
+    int nitems;
+
+    unsigned char allow_int;
+    unsigned char val_long;
+    int nitems_invalid_report;
+} Clp_StringList;
+
+
+static const Clp_Option clp_option_sentinel[] = {
+    {"", 0, Clp_NotOption, 0, 0},
+    {"", 0, Clp_Done, 0, 0},
+    {"", 0, Clp_BadOption, 0, 0},
+    {"", 0, Clp_Error, 0, 0}
+};
+
+
+static int parse_string(Clp_Parser *, const char *, int, void *);
+static int parse_int(Clp_Parser *, const char *, int, void *);
+static int parse_bool(Clp_Parser *, const char *, int, void *);
+static int parse_double(Clp_Parser *, const char *, int, void *);
+static int parse_string_list(Clp_Parser *, const char *, int, void *);
+
+static int ambiguity_error(Clp_Parser *, int, int *, const Clp_Option *,
+                          const Clp_InternOption *, const char *, const char *,
+                          ...);
+
+
+/*******
+ * utf8
+ **/
+
+#define U_REPLACEMENT 0xFFFD
+
+static char *
+encode_utf8(char *s, int n, int c)
+{
+    if (c < 0 || c >= 0x110000 || (c >= 0xD800 && c <= 0xDFFF))
+       c = U_REPLACEMENT;
+    if (c <= 0x7F && n >= 1)
+       *s++ = c;
+    else if (c <= 0x7FF && n >= 2) {
+       *s++ = 0xC0 | (c >> 6);
+       goto char1;
+    } else if (c <= 0xFFFF && n >= 3) {
+       *s++ = 0xE0 | (c >> 12);
+       goto char2;
+    } else if (n >= 4) {
+       *s++ = 0xF0 | (c >> 18);
+       *s++ = 0x80 | ((c >> 12) & 0x3F);
+      char2:
+       *s++ = 0x80 | ((c >> 6) & 0x3F);
+      char1:
+       *s++ = 0x80 | (c & 0x3F);
+    }
+    return s;
+}
+
+static int
+decode_utf8(const char *s, const char **cp)
+{
+    int c;
+    if ((unsigned char) *s <= 0x7F)            /* 1 byte:  0x000000-0x00007F */
+       c = *s++;
+    else if ((unsigned char) *s <= 0xC1)       /*   bad/overlong encoding */
+       goto replacement;
+    else if ((unsigned char) *s <= 0xDF) {     /* 2 bytes: 0x000080-0x0007FF */
+       if ((s[1] & 0xC0) != 0x80)              /*   bad encoding */
+           goto replacement;
+       c = (*s++ & 0x1F) << 6;
+       goto char1;
+    } else if ((unsigned char) *s <= 0xEF) {   /* 3 bytes: 0x000800-0x00FFFF */
+       if ((s[1] & 0xC0) != 0x80               /*   bad encoding */
+           || (s[2] & 0xC0) != 0x80            /*   bad encoding */
+           || ((unsigned char) *s == 0xE0      /*   overlong encoding */
+               && (s[1] & 0xE0) == 0x80)
+           || ((unsigned char) *s == 0xED      /*   encoded surrogate */
+               && (s[1] & 0xE0) == 0xA0))
+           goto replacement;
+       c = (*s++ & 0x0F) << 12;
+       goto char2;
+    } else if ((unsigned char) *s <= 0xF4) {   /* 4 bytes: 0x010000-0x10FFFF */
+       if ((s[1] & 0xC0) != 0x80               /*   bad encoding */
+           || (s[2] & 0xC0) != 0x80            /*   bad encoding */
+           || (s[3] & 0xC0) != 0x80            /*   bad encoding */
+           || ((unsigned char) *s == 0xF0      /*   overlong encoding */
+               && (s[1] & 0xF0) == 0x80)
+           || ((unsigned char) *s == 0xF4      /*   encoded value > 0x10FFFF */
+               && (unsigned char) s[1] >= 0x90))
+           goto replacement;
+       c = (*s++ & 0x07) << 18;
+       c += (*s++ & 0x3F) << 12;
+      char2:
+       c += (*s++ & 0x3F) << 6;
+      char1:
+       c += (*s++ & 0x3F);
+    } else {
+      replacement:
+       c = U_REPLACEMENT;
+       for (s++; (*s & 0xC0) == 0x80; s++)
+           /* nothing */;
+    }
+    if (cp)
+       *cp = s;
+    return c;
+}
+
+static int
+utf8_charlen(const char *s)
+{
+    const char *sout;
+    (void) decode_utf8(s, &sout);
+    return sout - s;
+}
+
+static int
+clp_utf8_charlen(const Clp_Internal *cli, const char *s)
+{
+    return (cli->utf8 ? utf8_charlen(s) : 1);
+}
+
+
+/*******
+ * Clp_NewParser, etc.
+ **/
+
+static int
+min_different_chars(const char *s, const char *t)
+     /* Returns the minimum number of bytes required to distinguish
+       s from t.
+       If s is shorter than t, returns strlen(s). */
+{
+    const char *sfirst = s;
+    while (*s && *t && *s == *t)
+       s++, t++;
+    if (!*s)
+       return s - sfirst;
+    else
+       return s - sfirst + 1;
+}
+
+static int
+long_as_short(const Clp_Internal *cli, const Clp_Option *o,
+             Clp_InternOption *io, int failure)
+{
+    if ((cli->long1pos || cli->long1neg) && io->ilong) {
+       const char *name = o->long_name + io->ilongoff;
+       if (cli->utf8) {
+           int c = decode_utf8(name, &name);
+           if (!*name && c && c != U_REPLACEMENT)
+               return c;
+       } else if (name[0] && !name[1])
+           return (unsigned char) name[0];
+    }
+    return failure;
+}
+
+static void
+compare_options(Clp_Parser *clp, const Clp_Option *o1, Clp_InternOption *io1,
+               const Clp_Option *o2, Clp_InternOption *io2)
+{
+    Clp_Internal *cli = clp->internal;
+    int short1, shortx1;
+
+    /* ignore meaningless combinations */
+    if ((!io1->ishort && !io1->ilong) || (!io2->ishort && !io2->ilong)
+       || !((io1->ipos && io2->ipos) || (io1->ineg && io2->ineg))
+       || o1->option_id == o2->option_id)
+       return;
+
+    /* look for duplication of short options */
+    short1 = (io1->ishort ? o1->short_name : -1);
+    shortx1 = long_as_short(cli, o1, io1, -2);
+    if (short1 >= 0 || shortx1 >= 0) {
+       int short2 = (io2->ishort ? o2->short_name : -3);
+       int shortx2 = long_as_short(cli, o2, io2, -4);
+       if (short1 == short2)
+           Clp_OptionError(clp, "CLP internal error: more than 1 option has short name %<%c%>", short1);
+       else if ((short1 == shortx2 || shortx1 == short2 || shortx1 == shortx2)
+                && ((io1->ipos && io2->ipos && cli->long1pos)
+                    || (io1->ineg && io2->ineg && cli->long1neg)))
+           Clp_OptionError(clp, "CLP internal error: 1-char long name conflicts with short name %<%c%>", (short1 == shortx2 ? shortx2 : shortx1));
+    }
+
+    /* analyze longest minimum match */
+    if (io1->ilong) {
+       const char *name1 = o1->long_name + io1->ilongoff;
+
+       /* long name's first character matches short name */
+       if (io2->ishort && !io1->iprefmatch) {
+           int name1char = (cli->utf8 ? decode_utf8(name1, 0) : (unsigned char) *name1);
+           if (name1char == o2->short_name) {
+               if (io1->ipos && io2->ipos)
+                   io1->lmmpos_short = 1;
+               if (io1->ineg && io2->ineg)
+                   io1->lmmneg_short = 1;
+           }
+       }
+
+       /* match long name to long name */
+       if (io2->ilong) {
+           const char *name2 = o2->long_name + io2->ilongoff;
+           if (strcmp(name1, name2) == 0)
+               Clp_OptionError(clp, "CLP internal error: duplicate long name %<%s%>", name1);
+           if (io1->ipos && io2->ipos && !strncmp(name1, name2, io1->lmmpos)
+               && (!io1->iprefmatch || strncmp(name1, name2, strlen(name1))))
+               io1->lmmpos = min_different_chars(name1, name2);
+           if (io1->ineg && io2->ineg && !strncmp(name1, name2, io1->lmmneg)
+               && (!io1->iprefmatch || strncmp(name1, name2, strlen(name1))))
+               io1->lmmneg = min_different_chars(name1, name2);
+       }
+    }
+}
+
+static void
+calculate_lmm(Clp_Parser *clp, const Clp_Option *opt, Clp_InternOption *iopt, int nopt)
+{
+    int i, j;
+    for (i = 0; i < nopt; ++i) {
+       iopt[i].lmmpos = iopt[i].lmmneg = 1;
+       iopt[i].lmmpos_short = iopt[i].lmmneg_short = 0;
+       for (j = 0; j < nopt; ++j)
+           compare_options(clp, &opt[i], &iopt[i], &opt[j], &iopt[j]);
+    }
+}
+
+/** @param argc number of arguments
+ * @param argv argument array
+ * @param nopt number of option definitions
+ * @param opt option definition array
+ * @return the parser
+ *
+ * The new Clp_Parser that will parse the arguments in @a argv according to
+ * the option definitions in @a opt.
+ *
+ * The Clp_Parser is created with the following characteristics:
+ *
+ * <ul>
+ * <li>The "-" character introduces short options (<tt>Clp_SetOptionChar(clp,
+ * '-', Clp_Short)</tt>).</li>
+ * <li>Clp_ProgramName is set from the first argument in @a argv, if any.  The
+ * first argument returned by Clp_Next() will be the second argument in @a
+ * argv.  Note that this behavior differs from Clp_SetArguments.</li>
+ * <li>UTF-8 support is on iff the <tt>LANG</tt> environment variable contains
+ * one of the substrings "UTF-8", "UTF8", or "utf8".  Override this with
+ * Clp_SetUTF8().</li>
+ * <li>The Clp_ValString, Clp_ValStringNotOption, Clp_ValInt, Clp_ValUnsigned,
+ * Clp_ValLong, Clp_ValUnsignedLong, Clp_ValBool, and Clp_ValDouble types are
+ * installed.</li>
+ * <li>Errors are reported to standard error.</li>
+ * </ul>
+ *
+ * You may also create a Clp_Parser with no arguments or options
+ * (<tt>Clp_NewParser(0, 0, 0, 0)</tt>) and set the arguments and options
+ * later.
+ *
+ * Returns NULL if there isn't enough memory to construct the parser.
+ *
+ * @note The CLP library will not modify the contents of @a argv or @a opt.
+ * The calling program must not modify @a opt.  It may modify @a argv in
+ * limited cases.
+ */
+Clp_Parser *
+Clp_NewParser(int argc, const char * const *argv, int nopt, const Clp_Option *opt)
+{
+    Clp_Parser *clp = (Clp_Parser *)malloc(sizeof(Clp_Parser));
+    Clp_Internal *cli = (Clp_Internal *)malloc(sizeof(Clp_Internal));
+    Clp_InternOption *iopt = (Clp_InternOption *)malloc(sizeof(Clp_InternOption) * nopt);
+    if (cli)
+       cli->valtype = (Clp_ValType *)malloc(sizeof(Clp_ValType) * Clp_InitialValType);
+    if (!clp || !cli || !iopt || !cli->valtype)
+       goto failed;
+
+    clp->option = &clp_option_sentinel[-Clp_Done];
+    clp->negated = 0;
+    clp->have_val = 0;
+    clp->vstr = 0;
+    clp->user_data = 0;
+    clp->internal = cli;
+
+    cli->opt = opt;
+    cli->nopt = nopt;
+    cli->iopt = iopt;
+    cli->opt_generation = 0;
+    cli->error_handler = 0;
+
+    /* Assign program name (now so we can call Clp_OptionError) */
+    if (argc > 0) {
+       const char *slash = strrchr(argv[0], '/');
+       cli->program_name = slash ? slash + 1 : argv[0];
+    } else
+       cli->program_name = 0;
+
+    /* Assign arguments, skipping program name */
+    Clp_SetArguments(clp, argc - 1, argv + 1);
+
+    /* Initialize UTF-8 status and option classes */
+    {
+       char *s = getenv("LANG");
+       cli->utf8 = (s && (strstr(s, "UTF-8") != 0 || strstr(s, "UTF8") != 0
+                          || strstr(s, "utf8") != 0));
+    }
+    cli->oclass[0].c = '-';
+    cli->oclass[0].type = Clp_Short;
+    cli->noclass = 1;
+    cli->long1pos = cli->long1neg = 0;
+
+    /* Add default type parsers */
+    cli->nvaltype = 0;
+    Clp_AddType(clp, Clp_ValString, 0, parse_string, 0);
+    Clp_AddType(clp, Clp_ValStringNotOption, Clp_DisallowOptions, parse_string, 0);
+    Clp_AddType(clp, Clp_ValInt, 0, parse_int, (void*) (uintptr_t) 0);
+    Clp_AddType(clp, Clp_ValUnsigned, 0, parse_int, (void*) (uintptr_t) 1);
+    Clp_AddType(clp, Clp_ValLong, 0, parse_int, (void*) (uintptr_t) 2);
+    Clp_AddType(clp, Clp_ValUnsignedLong, 0, parse_int, (void*) (uintptr_t) 3);
+    Clp_AddType(clp, Clp_ValBool, 0, parse_bool, 0);
+    Clp_AddType(clp, Clp_ValDouble, 0, parse_double, 0);
+
+    /* Set options */
+    Clp_SetOptions(clp, nopt, opt);
+
+    return clp;
+
+  failed:
+    if (cli && cli->valtype)
+       free(cli->valtype);
+    if (cli)
+       free(cli);
+    if (clp)
+       free(clp);
+    if (iopt)
+       free(iopt);
+    return 0;
+}
+
+/** @param clp the parser
+ *
+ * All memory associated with @a clp is freed. */
+void
+Clp_DeleteParser(Clp_Parser *clp)
+{
+    int i;
+    Clp_Internal *cli;
+    if (!clp)
+       return;
+
+    cli = clp->internal;
+
+    /* get rid of any string list types */
+    for (i = 0; i < cli->nvaltype; i++)
+       if (cli->valtype[i].func == parse_string_list) {
+           Clp_StringList *clsl = (Clp_StringList *)cli->valtype[i].user_data;
+           free(clsl->items);
+           free(clsl->iopt);
+           free(clsl);
+       }
+
+    free(cli->valtype);
+    free(cli->iopt);
+    free(cli);
+    free(clp);
+}
+
+
+/** @param clp the parser
+ * @param errh error handler function
+ * @return previous error handler function
+ *
+ * The error handler function is called when CLP encounters an error while
+ * parsing the command line.  It is called with the arguments "<tt>(*errh)(@a
+ * clp, s)</tt>", where <tt>s</tt> is a description of the error terminated by
+ * a newline.  The <tt>s</tt> descriptions produced by CLP itself are prefixed
+ * by the program name, if any. */
+Clp_ErrorHandler
+Clp_SetErrorHandler(Clp_Parser *clp, void (*errh)(Clp_Parser *, const char *))
+{
+    Clp_Internal *cli = clp->internal;
+    Clp_ErrorHandler old = cli->error_handler;
+    cli->error_handler = errh;
+    return old;
+}
+
+/** @param clp the parser
+ * @param utf8 does the parser support UTF-8?
+ * @return previous UTF-8 mode
+ *
+ * In UTF-8 mode, all input strings (arguments and long names for options) are
+ * assumed to be encoded via UTF-8, and all character names
+ * (Clp_SetOptionChar() and short names for options) may cover the whole
+ * Unicode range.  Out of UTF-8 mode, all input strings are treated as binary,
+ * and all character names must be unsigned char values.
+ *
+ * Furthermore, error messages in UTF-8 mode may contain Unicode quote
+ * characters. */
+int
+Clp_SetUTF8(Clp_Parser *clp, int utf8)
+{
+    Clp_Internal *cli = clp->internal;
+    int old_utf8 = cli->utf8;
+    cli->utf8 = utf8;
+    calculate_lmm(clp, cli->opt, cli->iopt, cli->nopt);
+    return old_utf8;
+}
+
+/** @param clp the parser
+ * @param c character
+ * @return option character treatment
+ *
+ * Returns an integer specifying how CLP treats arguments that begin
+ * with character @a c.  See Clp_SetOptionChar for possibilities.
+ */
+int
+Clp_OptionChar(Clp_Parser *clp, int c)
+{
+    Clp_Internal *cli = clp->internal;
+    int i, oclass = 0;
+    if (cli->noclass > 0 && cli->oclass[0].c == 0)
+       oclass = cli->oclass[0].type;
+    for (i = 0; i < cli->noclass; ++i)
+       if (cli->oclass[i].c == c)
+           oclass = cli->oclass[i].type;
+    return oclass;
+}
+
+/** @param clp the parser
+ * @param c character
+ * @param type option character treatment
+ * @return previous option character treatment, or -1 on error
+ *
+ * @a type specifies how CLP treats arguments that begin with character @a c.
+ * Possibilities are:
+ *
+ * <dl>
+ * <dt>Clp_NotOption (or 0)</dt>
+ * <dd>The argument cannot be an option.</dd>
+ * <dt>Clp_Long</dt>
+ * <dd>The argument is a long option.</dd>
+ * <dt>Clp_Short</dt>
+ * <dd>The argument is a set of short options.</dd>
+ * <dt>Clp_Short|Clp_Long</dt>
+ * <dd>The argument is either a long option or, if no matching long option is
+ * found, a set of short options.</dd>
+ * <dt>Clp_LongNegated</dt>
+ * <dd>The argument is a negated long option.  For example, after
+ * Clp_SetOptionChar(@a clp, '^', Clp_LongNegated), the argument "^foo" is
+ * equivalent to "--no-foo".</dd>
+ * <dt>Clp_ShortNegated</dt>
+ * <dd>The argument is a set of negated short options.</dd>
+ * <dt>Clp_ShortNegated|Clp_LongNegated</dt>
+ * <dd>The argument is either a negated long option or, if no matching long
+ * option is found, a set of negated short options.</dd>
+ * <dt>Clp_LongImplicit</dt>
+ * <dd>The argument may be a long option, where the character @a c is actually
+ * part of the long option name.  For example, after Clp_SetOptionChar(@a clp,
+ * 'f', Clp_LongImplicit), the argument "foo" may be equivalent to
+ * "--foo".</dd>
+ * </dl>
+ *
+ * In UTF-8 mode, @a c may be any Unicode character.  Otherwise, @a c must be
+ * an unsigned char value.  The special character 0 assigns @a type to @em
+ * every character.
+ *
+ * It is an error if @a c is out of range, @a type is illegal, or there are
+ * too many character definitions stored in @a clp already.  The function
+ * returns -1 on error.
+ *
+ * A double hyphen "--" always introduces a long option.  This behavior cannot
+ * currently be changed with Clp_SetOptionChar().
+ */
+int
+Clp_SetOptionChar(Clp_Parser *clp, int c, int type)
+{
+    int i, long1pos, long1neg;
+    int old = Clp_OptionChar(clp, c);
+    Clp_Internal *cli = clp->internal;
+
+    if (type != Clp_NotOption && type != Clp_Short && type != Clp_Long
+       && type != Clp_ShortNegated && type != Clp_LongNegated
+       && type != Clp_LongImplicit && type != (Clp_Short | Clp_Long)
+       && type != (Clp_ShortNegated | Clp_LongNegated))
+       return -1;
+    if (c < 0 || c >= (cli->utf8 ? 0x110000 : 256))
+       return -1;
+
+    if (c == 0)
+       cli->noclass = 0;
+    for (i = 0; i < cli->noclass; ++i)
+       if (cli->oclass[i].c == c)
+           break;
+    if (i == Clp_OclassSize)
+       return -1;
+
+    cli->oclass[i].c = c;
+    cli->oclass[i].type = type;
+    if (cli->noclass == i)
+       cli->noclass = i + 1;
+
+    long1pos = long1neg = 0;
+    for (i = 0; i < cli->noclass; ++i) {
+       if ((cli->oclass[i].type & Clp_Short)
+           && (cli->oclass[i].type & Clp_Long))
+           long1pos = 1;
+       if ((cli->oclass[i].type & Clp_ShortNegated)
+           && (cli->oclass[i].type & Clp_LongNegated))
+           long1neg = 1;
+    }
+
+    if (long1pos != cli->long1pos || long1neg != cli->long1neg) {
+       /* Must recheck option set */
+       cli->long1pos = long1pos;
+       cli->long1neg = long1neg;
+       calculate_lmm(clp, cli->opt, cli->iopt, cli->nopt);
+    }
+
+    return old;
+}
+
+/** @param clp the parser
+ * @param nopt number of option definitions
+ * @param opt option definition array
+ * @return 0 on success, -1 on failure
+ *
+ * Installs the option definitions in @a opt.  Future option parsing will
+ * use @a opt to search for options.
+ *
+ * Also checks @a opt's option definitions for validity.  "CLP internal
+ * errors" are reported via Clp_OptionError() if:
+ *
+ * <ul>
+ * <li>An option has a negative ID.</li>
+ * <li>Two different short options have the same name.</li>
+ * <li>Two different long options have the same name.</li>
+ * <li>A short and a long option are ambiguous, in that some option character
+ * might introduce either a short or a long option (e.g., Clp_SetOptionChar(@a
+ * clp, '-', Clp_Long|Clp_Short)), and a short name equals a long name.</li>
+ * </ul>
+ *
+ * If necessary memory cannot be allocated, this function returns -1 without
+ * modifying the parser.
+ *
+ * @note The CLP library will not modify the contents of @a argv or @a opt.
+ * The calling program must not modify @a opt either until another call to
+ * Clp_SetOptions() or the parser is destroyed.
+ */
+int
+Clp_SetOptions(Clp_Parser *clp, int nopt, const Clp_Option *opt)
+{
+    Clp_Internal *cli = clp->internal;
+    Clp_InternOption *iopt;
+    int i;
+    static unsigned opt_generation = 0;
+
+    if (nopt > cli->nopt) {
+       iopt = (Clp_InternOption *)malloc(sizeof(Clp_InternOption) * nopt);
+       if (!iopt)
+           return -1;
+       free(cli->iopt);
+       cli->iopt = iopt;
+    }
+
+    cli->opt = opt;
+    cli->nopt = nopt;
+    cli->opt_generation = ++opt_generation;
+    iopt = cli->iopt;
+    cli->current_option = -1;
+
+    /* Massage the options to make them usable */
+    for (i = 0; i < nopt; ++i) {
+       memset(&iopt[i], 0, sizeof(iopt[i]));
+
+       /* Ignore negative option_ids, which are internal to CLP */
+       if (opt[i].option_id < 0) {
+           Clp_OptionError(clp, "CLP internal error: option %d has negative option_id", i);
+           iopt[i].ilong = iopt[i].ishort = iopt[i].ipos = iopt[i].ineg = 0;
+           continue;
+       }
+
+       /* Set flags based on input flags */
+       iopt[i].ilong = (opt[i].long_name != 0 && opt[i].long_name[0] != 0);
+       iopt[i].ishort = (opt[i].short_name > 0
+                         && opt[i].short_name < (cli->utf8 ? 0x110000 : 256));
+       iopt[i].ipos = 1;
+       iopt[i].ineg = (opt[i].flags & Clp_Negate) != 0;
+       iopt[i].imandatory = (opt[i].flags & Clp_Mandatory) != 0;
+       iopt[i].ioptional = (opt[i].flags & Clp_Optional) != 0;
+       iopt[i].iprefmatch = (opt[i].flags & Clp_PreferredMatch) != 0;
+       iopt[i].ilongoff = 0;
+
+       /* Enforce invariants */
+       if (opt[i].val_type <= 0)
+           iopt[i].imandatory = iopt[i].ioptional = 0;
+       if (opt[i].val_type > 0 && !iopt[i].ioptional)
+           iopt[i].imandatory = 1;
+
+       /* Options that start with 'no-' should be changed to OnlyNegated */
+       if (iopt[i].ilong && strncmp(opt[i].long_name, "no-", 3) == 0) {
+           iopt[i].ipos = 0;
+           iopt[i].ineg = 1;
+           iopt[i].ilongoff = 3;
+           if (strncmp(opt[i].long_name + 3, "no-", 3) == 0)
+               Clp_OptionError(clp, "CLP internal error: option %d begins with \"no-no-\"", i);
+       } else if (opt[i].flags & Clp_OnlyNegated) {
+           iopt[i].ipos = 0;
+           iopt[i].ineg = 1;
+       }
+    }
+
+    /* Check option set */
+    calculate_lmm(clp, opt, iopt, nopt);
+
+    return 0;
+}
+
+/** @param clp the parser
+ * @param argc number of arguments
+ * @param argv argument array
+ *
+ * Installs the arguments in @a argv for parsing.  Future option parsing will
+ * analyze @a argv.
+ *
+ * Unlike Clp_NewParser(), this function does not treat @a argv[0] specially.
+ * The first subsequent call to Clp_Next() will analyze @a argv[0].
+ *
+ * This function also sets option processing to on, as by
+ * Clp_SetOptionProcessing(@a clp, 1).
+ *
+ * @note The CLP library will not modify the contents of @a argv.  The calling
+ * program should not generally modify the element of @a argv that CLP is
+ * currently analyzing.
+ */
+void
+Clp_SetArguments(Clp_Parser *clp, int argc, const char * const *argv)
+{
+    Clp_Internal *cli = clp->internal;
+
+    cli->argc = argc + 1;
+    cli->argv = argv - 1;
+
+    cli->is_short = 0;
+    cli->whole_negated = 0;
+    cli->option_processing = 1;
+    cli->current_option = -1;
+}
+
+
+/** @param clp the parser
+ * @param on whether to search for options
+ * @return previous option processing setting
+ *
+ * When option processing is off, every call to Clp_Next() returns
+ * Clp_NotOption.  By default the option <tt>"--"</tt> turns off option
+ * processing and is otherwise ignored.
+ */
+int
+Clp_SetOptionProcessing(Clp_Parser *clp, int on)
+{
+    Clp_Internal *cli = clp->internal;
+    int old = cli->option_processing;
+    cli->option_processing = on;
+    return old;
+}
+
+
+/*******
+ * functions for Clp_Option lists
+ **/
+
+/* the ever-glorious argcmp */
+
+static int
+argcmp(const char *ref, const char *arg, int min_match, int fewer_dashes)
+     /* Returns 0 if ref and arg don't match.
+       Returns -1 if ref and arg match, but fewer than min_match characters.
+       Returns len if ref and arg match min_match or more characters;
+       len is the number of characters that matched in arg.
+       Allows arg to contain fewer dashes than ref iff fewer_dashes != 0.
+
+       Examples:
+       argcmp("x", "y", 1, 0)  -->  0  / just plain wrong
+       argcmp("a", "ax", 1, 0) -->  0  / ...even though min_match == 1
+                                       and the 1st chars match
+       argcmp("box", "bo", 3, 0) --> -1        / ambiguous
+       argcmp("cat", "c=3", 1, 0) -->  1       / handles = arguments
+       */
+{
+    const char *refstart = ref;
+    const char *argstart = arg;
+    assert(min_match > 0);
+
+  compare:
+    while (*ref && *arg && *arg != '=' && *ref == *arg)
+       ref++, arg++;
+
+    /* Allow arg to contain fewer dashes than ref */
+    if (fewer_dashes && *ref == '-' && ref[1] && ref[1] == *arg) {
+       ref++;
+       goto compare;
+    }
+
+    if (*arg && *arg != '=')
+       return 0;
+    else if (ref - refstart < min_match)
+       return -1;
+    else
+       return arg - argstart;
+}
+
+static int
+find_prefix_opt(Clp_Parser *clp, const char *arg,
+               int nopt, const Clp_Option *opt,
+               const Clp_InternOption *iopt,
+               int *ambiguous, int *ambiguous_values)
+     /* Looks for an unambiguous match of 'arg' against one of the long
+        options in 'opt'. Returns positive if it finds one; otherwise, returns
+        -1 and possibly changes 'ambiguous' and 'ambiguous_values' to keep
+        track of at most MAX_AMBIGUOUS_VALUES possibilities. */
+{
+    int i, fewer_dashes = 0, first_ambiguous = *ambiguous;
+    int negated = clp && clp->negated;
+    int first_charlen = (clp ? clp_utf8_charlen(clp->internal, arg) : 1);
+
+  retry:
+    for (i = 0; i < nopt; i++) {
+       int len, lmm;
+       if (!iopt[i].ilong || (negated ? !iopt[i].ineg : !iopt[i].ipos))
+           continue;
+
+       lmm = (negated ? iopt[i].lmmneg : iopt[i].lmmpos);
+       if (clp && clp->internal->could_be_short
+           && (negated ? iopt[i].lmmneg_short : iopt[i].lmmpos_short))
+           lmm = (first_charlen >= lmm ? first_charlen + 1 : lmm);
+       len = argcmp(opt[i].long_name + iopt[i].ilongoff, arg, lmm, fewer_dashes);
+       if (len > 0)
+           return i;
+       else if (len < 0) {
+           if (*ambiguous < MAX_AMBIGUOUS_VALUES)
+               ambiguous_values[*ambiguous] = i;
+           (*ambiguous)++;
+       }
+    }
+
+    /* If there were no partial matches, try again with fewer_dashes true */
+    if (*ambiguous == first_ambiguous && !fewer_dashes) {
+       fewer_dashes = 1;
+       goto retry;
+    }
+
+    return -1;
+}
+
+
+/*****
+ * Argument parsing
+ **/
+
+static int
+val_type_binsearch(Clp_Internal *cli, int val_type)
+{
+    unsigned l = 0, r = cli->nvaltype;
+    while (l < r) {
+       unsigned m = l + (r - l) / 2;
+       if (cli->valtype[m].val_type == val_type)
+           return m;
+       else if (cli->valtype[m].val_type < val_type)
+           l = m + 1;
+       else
+           r = m;
+    }
+    return l;
+}
+
+/** @param clp the parser
+ * @param val_type value type ID
+ * @param flags value type flags
+ * @param parser parser function
+ * @param user_data user data for @a parser function
+ * @return 0 on success, -1 on failure
+ *
+ * Defines argument type @a val_type in parser @a clp.  The parsing function
+ * @a parser will be passed argument values for type @a val_type.  It should
+ * parse the argument into values (usually in @a clp->val, but sometimes
+ * elsewhere), report errors if necessary, and return whether the parse was
+ * successful.
+ *
+ * Any prior argument parser match @a val_type is removed.  @a val_type must
+ * be greater than zero.
+ *
+ * @a flags specifies additional parsing flags.  At the moment the only
+ * relevant flag is Clp_DisallowOptions, which means that separated values
+ * must not look like options.  For example, assume argument
+ * <tt>--a</tt>/<tt>-a</tt> has mandatory value type Clp_ValStringNotOption
+ * (which has Clp_DisallowOptions).  Then:
+ *
+ * <ul>
+ * <li><tt>--a=--b</tt> will parse with value <tt>--b</tt>.</li>
+ * <li><tt>-a--b</tt> will parse with value <tt>--b</tt>.</li>
+ * <li><tt>--a --b</tt> will not parse, since the mandatory value looks like
+ * an option.</li>
+ * <li><tt>-a --b</tt> will not parse, since the mandatory value looks like
+ * an option.</li>
+ * </ul>
+ */
+int
+Clp_AddType(Clp_Parser *clp, int val_type, int flags,
+           Clp_ValParseFunc parser, void *user_data)
+{
+    Clp_Internal *cli = clp->internal;
+    int vtpos;
+
+    if (val_type <= 0 || !parser)
+       return -1;
+
+    vtpos = val_type_binsearch(cli, val_type);
+
+    if (vtpos == cli->nvaltype || cli->valtype[vtpos].val_type != val_type) {
+       if (cli->nvaltype != 0 && (cli->nvaltype % Clp_InitialValType) == 0) {
+           Clp_ValType *new_valtype =
+               (Clp_ValType *) realloc(cli->valtype, sizeof(Clp_ValType) * (cli->nvaltype + Clp_InitialValType));
+           if (!new_valtype)
+               return -1;
+           cli->valtype = new_valtype;
+       }
+       memmove(&cli->valtype[vtpos + 1], &cli->valtype[vtpos],
+               sizeof(Clp_ValType) * (cli->nvaltype - vtpos));
+       cli->nvaltype++;
+       cli->valtype[vtpos].func = 0;
+    }
+
+    if (cli->valtype[vtpos].func == parse_string_list) {
+       Clp_StringList *clsl = (Clp_StringList *) cli->valtype[vtpos].user_data;
+       free(clsl->items);
+       free(clsl->iopt);
+       free(clsl);
+    }
+
+    cli->valtype[vtpos].val_type = val_type;
+    cli->valtype[vtpos].func = parser;
+    cli->valtype[vtpos].flags = flags;
+    cli->valtype[vtpos].user_data = user_data;
+    return 0;
+}
+
+
+/*******
+ * Default argument parsers
+ **/
+
+static int
+parse_string(Clp_Parser *clp, const char *arg, int complain, void *user_data)
+{
+    (void)complain, (void)user_data;
+    clp->val.s = arg;
+    return 1;
+}
+
+static int
+parse_int(Clp_Parser* clp, const char* arg, int complain, void* user_data)
+{
+    const char *val;
+    uintptr_t type = (uintptr_t) user_data;
+    if (*arg == 0 || isspace((unsigned char) *arg)
+       || ((type & 1) && *arg == '-'))
+       val = arg;
+    else if (type & 1) { /* unsigned */
+#if HAVE_STRTOUL
+       clp->val.ul = strtoul(arg, (char **) &val, 0);
+#else
+       /* don't bother really trying to do it right */
+       if (arg[0] == '-')
+           val = arg;
+       else
+           clp->val.l = strtol(arg, (char **) &val, 0);
+#endif
+    } else
+       clp->val.l = strtol(arg, (char **) &val, 0);
+    if (type <= 1)
+        clp->val.u = (unsigned) clp->val.ul;
+    if (*arg != 0 && *val == 0)
+       return 1;
+    else {
+        if (complain) {
+            const char *message = type & 1
+                ? "%<%O%> expects a nonnegative integer, not %<%s%>"
+                : "%<%O%> expects an integer, not %<%s%>";
+            Clp_OptionError(clp, message, arg);
+        }
+        return 0;
+    }
+}
+
+static int
+parse_double(Clp_Parser *clp, const char *arg, int complain, void *user_data)
+{
+    const char *val;
+    (void)user_data;
+    if (*arg == 0 || isspace((unsigned char) *arg))
+       val = arg;
+    else
+       clp->val.d = strtod(arg, (char **) &val);
+    if (*arg != 0 && *val == 0)
+       return 1;
+    else {
+        if (complain)
+            Clp_OptionError(clp, "%<%O%> expects a real number, not %<%s%>", arg);
+       return 0;
+    }
+}
+
+static int
+parse_bool(Clp_Parser *clp, const char *arg, int complain, void *user_data)
+{
+    int i;
+    char lcarg[6];
+    (void)user_data;
+    if (strlen(arg) > 5 || strchr(arg, '=') != 0)
+       goto error;
+
+    for (i = 0; arg[i] != 0; i++)
+       lcarg[i] = tolower((unsigned char) arg[i]);
+    lcarg[i] = 0;
+
+    if (argcmp("yes", lcarg, 1, 0) > 0
+       || argcmp("true", lcarg, 1, 0) > 0
+       || argcmp("1", lcarg, 1, 0) > 0) {
+       clp->val.i = 1;
+       return 1;
+    } else if (argcmp("no", lcarg, 1, 0) > 0
+              || argcmp("false", lcarg, 1, 0) > 0
+              || argcmp("1", lcarg, 1, 0) > 0) {
+       clp->val.i = 0;
+       return 1;
+    }
+
+  error:
+    if (complain)
+       Clp_OptionError(clp, "%<%O%> expects a true-or-false value, not %<%s%>", arg);
+    return 0;
+}
+
+
+/*****
+ * Clp_AddStringListType
+ **/
+
+static int
+parse_string_list(Clp_Parser *clp, const char *arg, int complain, void *user_data)
+{
+    Clp_StringList *sl = (Clp_StringList *)user_data;
+    int idx, ambiguous = 0;
+    int ambiguous_values[MAX_AMBIGUOUS_VALUES + 1];
+
+    /* actually look for a string value */
+    idx = find_prefix_opt
+       (0, arg, sl->nitems, sl->items, sl->iopt,
+        &ambiguous, ambiguous_values);
+    if (idx >= 0) {
+       clp->val.i = sl->items[idx].option_id;
+        if (sl->val_long)
+            clp->val.l = clp->val.i;
+        return 1;
+    }
+
+    if (sl->allow_int) {
+       if (parse_int(clp, arg, 0, (void*) (uintptr_t) (sl->val_long ? 2 : 0)))
+           return 1;
+    }
+
+    if (complain) {
+       const char *complaint = (ambiguous ? "ambiguous" : "invalid");
+       if (!ambiguous) {
+           ambiguous = sl->nitems_invalid_report;
+           for (idx = 0; idx < ambiguous; idx++)
+               ambiguous_values[idx] = idx;
+       }
+       return ambiguity_error
+           (clp, ambiguous, ambiguous_values, sl->items, sl->iopt,
+            "", "option %<%V%> is %s", complaint);
+    } else
+       return 0;
+}
+
+
+static int
+finish_string_list(Clp_Parser *clp, int val_type, int flags,
+                  Clp_Option *items, int nitems, int itemscap)
+{
+    int i;
+    Clp_StringList *clsl = (Clp_StringList *)malloc(sizeof(Clp_StringList));
+    Clp_InternOption *iopt = (Clp_InternOption *)malloc(sizeof(Clp_InternOption) * nitems);
+    if (!clsl || !iopt)
+       goto error;
+
+    clsl->items = items;
+    clsl->iopt = iopt;
+    clsl->nitems = nitems;
+    clsl->allow_int = (flags & Clp_AllowNumbers) != 0;
+    clsl->val_long = (flags & Clp_StringListLong) != 0;
+
+    if (nitems < MAX_AMBIGUOUS_VALUES && nitems < itemscap && clsl->allow_int) {
+       items[nitems].long_name = "any integer";
+       clsl->nitems_invalid_report = nitems + 1;
+    } else if (nitems > MAX_AMBIGUOUS_VALUES + 1)
+       clsl->nitems_invalid_report = MAX_AMBIGUOUS_VALUES + 1;
+    else
+       clsl->nitems_invalid_report = nitems;
+
+    for (i = 0; i < nitems; i++) {
+       iopt[i].ilong = iopt[i].ipos = 1;
+       iopt[i].ishort = iopt[i].ineg = iopt[i].ilongoff = iopt[i].iprefmatch = 0;
+    }
+    calculate_lmm(clp, items, iopt, nitems);
+
+    if (Clp_AddType(clp, val_type, 0, parse_string_list, clsl) >= 0)
+       return 0;
+
+  error:
+    if (clsl)
+       free(clsl);
+    if (iopt)
+       free(iopt);
+    return -1;
+}
+
+/** @param clp the parser
+ * @param val_type value type ID
+ * @param flags string list flags
+ * @return 0 on success, -1 on failure
+ *
+ * Defines argument type @a val_type in parser @a clp.  The parsing function
+ * sets @a clp->val.i to an integer.  The value string is matched against
+ * strings provided in the ellipsis arguments.  For example, the
+ * Clp_AddStringListType() call below has the same effect as the
+ * Clp_AddStringListTypeVec() call:
+ *
+ * For example:
+ * @code
+ * Clp_AddStringListType(clp, 100, Clp_AllowNumbers, "cat", 1,
+ *                       "cattle", 2, "dog", 3, (const char *) NULL);
+ *
+ * const char * const strs[] = { "cat", "cattle", "dog" };
+ * const int vals[]          = { 1,     2,        3     };
+ * Clp_AddStringListTypeVec(clp, 100, Clp_AllowNumbers, 3, strs, vals);
+ * @endcode
+ *
+ * @note The CLP library will not modify any of the passed-in strings.  The
+ * calling program must not modify or free them either until the parser is
+ * destroyed.
+ */
+int
+Clp_AddStringListType(Clp_Parser *clp, int val_type, int flags, ...)
+{
+    int nitems = 0;
+    int itemscap = 5;
+    Clp_Option *items = (Clp_Option *)malloc(sizeof(Clp_Option) * itemscap);
+
+    va_list val;
+    va_start(val, flags);
+
+    if (!items)
+       goto error;
+
+    /* slurp up the arguments */
+    while (1) {
+       int value;
+       char *name = va_arg(val, char *);
+       if (!name)
+           break;
+        if (flags & Clp_StringListLong) {
+            long lvalue = va_arg(val, long);
+            value = (int) lvalue;
+            assert(value == lvalue);
+        } else
+            value = va_arg(val, int);
+
+       if (nitems >= itemscap) {
+           Clp_Option *new_items;
+           itemscap *= 2;
+           new_items = (Clp_Option *)realloc(items, sizeof(Clp_Option) * itemscap);
+           if (!new_items)
+               goto error;
+           items = new_items;
+       }
+
+       items[nitems].long_name = name;
+       items[nitems].option_id = value;
+       items[nitems].flags = 0;
+       nitems++;
+    }
+
+    va_end(val);
+    if (finish_string_list(clp, val_type, flags, items, nitems, itemscap) >= 0)
+       return 0;
+
+  error:
+    va_end(val);
+    if (items)
+       free(items);
+    return -1;
+}
+
+/** @param clp the parser
+ * @param val_type value type ID
+ * @param flags string list flags
+ * @param nstrs number of strings in list
+ * @param strs array of strings
+ * @param vals array of values
+ * @return 0 on success, -1 on failure
+ *
+ * Defines argument type @a val_type in parser @a clp.  The parsing function
+ * sets @a clp->val.i to an integer.  The value string is matched against the
+ * @a strs.  If there's a unique match, the corresponding entry from @a vals
+ * is returned.  Unique prefix matches also work.  Finally, if @a flags
+ * contains the Clp_AllowNumbers flag, then integers are also accepted.
+ *
+ * For example:
+ * @code
+ * const char * const strs[] = { "cat", "cattle", "dog" };
+ * const int vals[]          = { 1,     2,        3     };
+ * Clp_AddStringListTypeVec(clp, 100, Clp_AllowNumbers, 3, strs, vals);
+ * @endcode
+ *
+ * Say that option <tt>--animal</tt> takes value type 100.  Then:
+ *
+ * <ul>
+ * <li><tt>--animal=cat</tt> will succeed and set @a clp->val.i = 1.</li>
+ * <li><tt>--animal=cattle</tt> will succeed and set @a clp->val.i = 2.</li>
+ * <li><tt>--animal=dog</tt> will succeed and set @a clp->val.i = 3.</li>
+ * <li><tt>--animal=d</tt> will succeed and set @a clp->val.i = 3.</li>
+ * <li><tt>--animal=c</tt> will fail, since <tt>c</tt> is ambiguous.</li>
+ * <li><tt>--animal=4</tt> will succeed and set @a clp->val.i = 4.</li>
+ * </ul>
+ *
+ * @note The CLP library will not modify the contents of @a strs or @a vals.
+ * The calling program can modify the @a strs array, but the actual strings
+ * (for instance, @a strs[0] and @a strs[1]) must not be modified or freed
+ * until the parser is destroyed.
+ */
+int
+Clp_AddStringListTypeVec(Clp_Parser *clp, int val_type, int flags,
+                        int nstrs, const char * const *strs,
+                        const int *vals)
+     /* An alternate way to make a string list type. See Clp_AddStringListType
+       for the basics; this coalesces the strings and values into two arrays,
+       rather than spreading them out into a variable argument list. */
+{
+    int i;
+    int itemscap = (nstrs < 5 ? 5 : nstrs);
+    Clp_Option *items = (Clp_Option *)malloc(sizeof(Clp_Option) * itemscap);
+    if (!items)
+       return -1;
+
+    /* copy over items */
+    for (i = 0; i < nstrs; i++) {
+       items[i].long_name = strs[i];
+       items[i].option_id = vals[i];
+       items[i].flags = 0;
+    }
+
+    if (finish_string_list(clp, val_type, flags, items, nstrs, itemscap) >= 0)
+       return 0;
+    else {
+       free(items);
+       return -1;
+    }
+}
+
+
+/*******
+ * Returning information
+ **/
+
+const char *
+Clp_ProgramName(Clp_Parser *clp)
+{
+    return clp->internal->program_name;
+}
+
+/** @param clp the parser
+ * @param name new program name
+ * @return previous program name
+ *
+ * The calling program should not modify or free @a name until @a clp itself
+ * is destroyed. */
+const char *
+Clp_SetProgramName(Clp_Parser *clp, const char *name)
+{
+    const char *old = clp->internal->program_name;
+    clp->internal->program_name = name;
+    return old;
+}
+
+
+/******
+ * Clp_ParserStates
+ **/
+
+/** @return the parser state
+ *
+ * A Clp_ParserState object can store a parsing state of a Clp_Parser object.
+ * This state specifies exactly how far the Clp_Parser has gotten in parsing
+ * an argument list.  The Clp_SaveParser() and Clp_RestoreParser() functions
+ * can be used to save this state and then restore it later, allowing a
+ * Clp_Parser to switch among argument lists.
+ *
+ * The initial state is empty, in that after Clp_RestoreParser(clp, state),
+ * Clp_Next(clp) would return Clp_Done.
+ *
+ * Parser states can be saved and restored among different parser objects.
+ *
+ * @sa Clp_DeleteParserState, Clp_SaveParser, Clp_RestoreParser
+ */
+Clp_ParserState *
+Clp_NewParserState(void)
+{
+    Clp_ParserState *state = (Clp_ParserState *)malloc(sizeof(Clp_ParserState));
+    if (state) {
+       state->argv = 0;
+       state->argc = 0;
+       state->option_chars[0] = 0;
+       state->xtext = 0;
+       state->option_processing = 0;
+       state->opt_generation = 0;
+       state->current_option = -1;
+       state->is_short = 0;
+       state->whole_negated = 0;
+       state->current_short = 0;
+       state->negated_by_no = 0;
+    }
+    return state;
+}
+
+/** @param state parser state
+ *
+ * The memory associated with @a state is freed.
+ */
+void
+Clp_DeleteParserState(Clp_ParserState *state)
+{
+    free(state);
+}
+
+/** @param clp the parser
+ * @param state parser state
+ * @sa Clp_NewParserState, Clp_RestoreParser
+ */
+void
+Clp_SaveParser(const Clp_Parser *clp, Clp_ParserState *state)
+{
+    Clp_Internal *cli = clp->internal;
+    state->argv = cli->argv;
+    state->argc = cli->argc;
+    memcpy(state->option_chars, cli->option_chars, Clp_OptionCharsSize);
+    state->xtext = cli->xtext;
+
+    state->option_processing = cli->option_processing;
+    state->opt_generation = cli->opt_generation;
+    state->current_option = cli->current_option;
+    state->is_short = cli->is_short;
+    state->whole_negated = cli->whole_negated;
+    state->current_short = cli->current_short;
+    state->negated_by_no = cli->negated_by_no;
+}
+
+
+/** @param clp the parser
+ * @param state parser state
+ *
+ * The parser state in @a state is restored into @a clp.  The next call to
+ * Clp_Next() will return the same result as it would have at the time @a
+ * state was saved (probably by Clp_SaveParser(@a clp, @a state)).
+ *
+ * A parser state contains information about arguments (argc and argv; see
+ * Clp_SetArguments()) and option processing (Clp_SetOptionProcessing()), but
+ * not about options (Clp_SetOptions()).  Changes to options and value types
+ * are preserved across Clp_RestoreParser().
+ *
+ * @sa Clp_NewParserState, Clp_SaveParser
+ */
+void
+Clp_RestoreParser(Clp_Parser *clp, const Clp_ParserState *state)
+{
+    Clp_Internal *cli = clp->internal;
+    cli->argv = state->argv;
+    cli->argc = state->argc;
+    memcpy(cli->option_chars, state->option_chars, Clp_OptionCharsSize);
+    cli->xtext = state->xtext;
+    cli->option_processing = state->option_processing;
+    cli->is_short = state->is_short;
+    cli->whole_negated = state->whole_negated;
+    cli->current_short = state->current_short;
+    cli->negated_by_no = state->negated_by_no;
+    if (cli->opt_generation == state->opt_generation)
+       cli->current_option = state->current_option;
+    else
+       cli->current_option = -1;
+}
+
+
+/*******
+ * Clp_Next and its helpers
+ **/
+
+static void
+set_option_text(Clp_Internal *cli, const char *text, int n_option_chars)
+{
+    assert(n_option_chars < Clp_OptionCharsSize);
+    memcpy(cli->option_chars, text, n_option_chars);
+    cli->option_chars[n_option_chars] = 0;
+    cli->xtext = text + n_option_chars;
+}
+
+static int
+get_oclass(Clp_Parser *clp, const char *text, int *ocharskip)
+{
+    int c;
+    if (clp->internal->utf8) {
+       const char *s;
+       c = decode_utf8(text, &s);
+       *ocharskip = s - text;
+    } else {
+       c = (unsigned char) text[0];
+       *ocharskip = 1;
+    }
+    return Clp_OptionChar(clp, c);
+}
+
+static int
+next_argument(Clp_Parser *clp, int want_argument)
+     /* Moves clp to the next argument.
+       Returns 1 if it finds another option.
+       Returns 0 if there aren't any more arguments.
+       Returns 0, sets clp->have_val = 1, and sets clp->vstr to the argument
+       if the next argument isn't an option.
+       If want_argument > 0, it'll look for an argument.
+       want_argument == 1: Accept arguments that start with Clp_NotOption
+               or Clp_LongImplicit.
+       want_argument == 2: Accept ALL arguments.
+
+       Where is the option stored when this returns?
+       Well, cli->argv[0] holds the whole of the next command line argument.
+       cli->option_chars holds a string: what characters began the option?
+       It is generally "-" or "--".
+       cli->text holds the text of the option:
+       for short options, cli->text[0] is the relevant character;
+       for long options, cli->text holds the rest of the option. */
+{
+    Clp_Internal *cli = clp->internal;
+    const char *text;
+    int oclass, ocharskip;
+
+    /* clear relevant flags */
+    clp->have_val = 0;
+    clp->vstr = 0;
+    cli->could_be_short = 0;
+
+    /* if we're in a string of short options, move up one char in the string */
+    if (cli->is_short) {
+       cli->xtext += clp_utf8_charlen(cli, cli->xtext);
+       if (cli->xtext[0] == 0)
+           cli->is_short = 0;
+       else if (want_argument > 0) {
+           /* handle -O[=]argument case */
+           clp->have_val = 1;
+           if (cli->xtext[0] == '=')
+               clp->vstr = cli->xtext + 1;
+           else
+               clp->vstr = cli->xtext;
+           cli->is_short = 0;
+           return 0;
+       }
+    }
+
+    /* if in short options, we're all set */
+    if (cli->is_short)
+       return 1;
+
+    /** if not in short options, move to the next argument **/
+    cli->whole_negated = 0;
+    cli->xtext = 0;
+
+    if (cli->argc <= 1)
+       return 0;
+
+    cli->argc--;
+    cli->argv++;
+    text = cli->argv[0];
+
+    if (want_argument > 1)
+       goto not_option;
+
+    if (text[0] == '-' && text[1] == '-') {
+       oclass = Clp_DoubledLong;
+       ocharskip = 2;
+    } else
+       oclass = get_oclass(clp, text, &ocharskip);
+
+    /* If this character could introduce either a short or a long option,
+       try a long option first, but remember that short's a possibility for
+       later. */
+    if ((oclass & (Clp_Short | Clp_ShortNegated))
+       && (oclass & (Clp_Long | Clp_LongNegated))) {
+       oclass &= ~(Clp_Short | Clp_ShortNegated);
+       if (text[ocharskip])
+           cli->could_be_short = 1;
+    }
+
+    switch (oclass) {
+
+      case Clp_Short:
+       cli->is_short = 1;
+       goto check_singleton;
+
+      case Clp_ShortNegated:
+       cli->is_short = 1;
+       cli->whole_negated = 1;
+       goto check_singleton;
+
+      case Clp_Long:
+       goto check_singleton;
+
+      case Clp_LongNegated:
+       cli->whole_negated = 1;
+       goto check_singleton;
+
+      check_singleton:
+       /* For options introduced with one character, option-char,
+          '[option-char]' alone is NOT an option. */
+       if (!text[ocharskip])
+           goto not_option;
+       set_option_text(cli, text, ocharskip);
+       break;
+
+      case Clp_LongImplicit:
+       /* LongImplict: option_chars == "" (since all chars are part of the
+          option); restore head -> text of option */
+       if (want_argument > 0)
+           goto not_option;
+       set_option_text(cli, text, 0);
+       break;
+
+      case Clp_DoubledLong:
+       set_option_text(cli, text, ocharskip);
+       break;
+
+      not_option:
+      case Clp_NotOption:
+       cli->is_short = 0;
+       clp->have_val = 1;
+       clp->vstr = text;
+       return 0;
+
+      default:
+       assert(0 /* CLP misconfiguration: bad option type */);
+
+    }
+
+    return 1;
+}
+
+
+static void
+switch_to_short_argument(Clp_Parser *clp)
+{
+    Clp_Internal *cli = clp->internal;
+    const char *text = cli->argv[0];
+    int ocharskip, oclass = get_oclass(clp, text, &ocharskip);
+    assert(cli->could_be_short);
+    cli->is_short = 1;
+    cli->whole_negated = (oclass & Clp_ShortNegated ? 1 : 0);
+    set_option_text(cli, cli->argv[0], ocharskip);
+}
+
+
+static int
+find_long(Clp_Parser *clp, const char *arg)
+     /* If arg corresponds to one of clp's options, finds that option &
+       returns it. If any argument is given after an = sign in arg, sets
+       clp->have_val = 1 and clp->vstr to that argument. Sets cli->ambiguous
+       to 1 iff there was no match because the argument was ambiguous. */
+{
+    Clp_Internal *cli = clp->internal;
+    int optno, len, lmm;
+    const Clp_Option *opt = cli->opt;
+    const Clp_InternOption *iopt;
+    int first_negative_ambiguous;
+
+    /* Look for a normal option. */
+    optno = find_prefix_opt
+       (clp, arg, cli->nopt, opt, cli->iopt,
+        &cli->ambiguous, cli->ambiguous_values);
+    if (optno >= 0)
+       goto worked;
+
+    /* If we can't find it, look for a negated option. */
+    /* I know this is silly, but it makes me happy to accept
+       --no-no-option as a double negative synonym for --option. :) */
+    first_negative_ambiguous = cli->ambiguous;
+    while (arg[0] == 'n' && arg[1] == 'o' && arg[2] == '-') {
+       arg += 3;
+       clp->negated = !clp->negated;
+       optno = find_prefix_opt
+           (clp, arg, cli->nopt, opt, cli->iopt,
+            &cli->ambiguous, cli->ambiguous_values);
+       if (optno >= 0)
+           goto worked;
+    }
+
+    /* No valid option was found; return 0. Mark the ambiguous values found
+       through '--no' by making them negative. */
+    {
+       int i, max = cli->ambiguous;
+       if (max > MAX_AMBIGUOUS_VALUES) max = MAX_AMBIGUOUS_VALUES;
+       for (i = first_negative_ambiguous; i < max; i++)
+           cli->ambiguous_values[i] = -cli->ambiguous_values[i] - 1;
+    }
+    return -1;
+
+  worked:
+    iopt = &cli->iopt[optno];
+    lmm = (clp->negated ? iopt->lmmneg : iopt->lmmpos);
+    if (cli->could_be_short
+       && (clp->negated ? iopt->lmmneg_short : iopt->lmmpos_short)) {
+       int first_charlen = clp_utf8_charlen(cli, arg);
+       lmm = (first_charlen >= lmm ? first_charlen + 1 : lmm);
+    }
+    len = argcmp(opt[optno].long_name + iopt->ilongoff, arg, lmm, 1);
+    assert(len > 0);
+    if (arg[len] == '=') {
+       clp->have_val = 1;
+       clp->vstr = arg + len + 1;
+    }
+    return optno;
+}
+
+
+static int
+find_short(Clp_Parser *clp, const char *text)
+     /* If short_name corresponds to one of clp's options, returns it. */
+{
+    Clp_Internal *cli = clp->internal;
+    const Clp_Option *opt = cli->opt;
+    const Clp_InternOption *iopt = cli->iopt;
+    int i, c;
+    if (clp->internal->utf8)
+       c = decode_utf8(text, 0);
+    else
+       c = (unsigned char) *text;
+
+    for (i = 0; i < cli->nopt; i++)
+       if (iopt[i].ishort && opt[i].short_name == c
+            && (!clp->negated || iopt[i].ineg)) {
+            clp->negated = clp->negated || !iopt[i].ipos;
+           return i;
+        }
+
+    return -1;
+}
+
+
+/** @param clp the parser
+ * @return option ID of next option
+ *
+ * Parse the next argument from the argument list, store information about
+ * that argument in the fields of @a clp, and return the option's ID.
+ *
+ * If an argument was successfully parsed, that option's ID is returned.
+ * Other possible return values are:
+ *
+ * <dl>
+ * <dt>Clp_Done</dt>
+ * <dd>There are no more arguments.</dd>
+ * <dt>Clp_NotOption</dt>
+ * <dd>The next argument was not an option.  The argument's text is @a
+ * clp->vstr (and @a clp->val.s).</dd>
+ * <dt>Clp_BadOption</dt>
+ * <dd>The next argument was a bad option: either an option that wasn't
+ * understood, or an option lacking a required value, or an option whose value
+ * couldn't be parsed.  The option has been skipped.</dd>
+ * <dt>Clp_Error</dt>
+ * <dd>There was an internal error.  This should never occur unless a user
+ * messes with, for example, a Clp_Option array.</dd>
+ * </dl>
+ *
+ * The fields of @a clp are set as follows.
+ *
+ * <dl>
+ * <dt><tt>negated</tt></dt>
+ * <dd>1 if the option was negated, 0 if it wasn't.</dd>
+ * <dt><tt>have_val</tt></dt>
+ * <dd>1 if the option had a value, 0 if it didn't.  Note that negated options
+ * are not allowed to have values.</dd>
+ * <dt><tt>vstr</tt></dt>
+ * <dd>The value string, if any.  NULL if there was no value.</dd>
+ * <dt><tt>val</tt></dt>
+ * <dd>An option's value type will parse the value string into this
+ * union.</dd>
+ * </dl>
+ *
+ * The parsed argument is shifted off the argument list, so that sequential
+ * calls to Clp_Next() step through the arugment list.
+ */
+int
+Clp_Next(Clp_Parser *clp)
+{
+    Clp_Internal *cli = clp->internal;
+    int optno;
+    const Clp_Option *opt;
+    Clp_ParserState clpsave;
+    int vtpos, complain;
+
+    /* Set up clp */
+    cli->current_option = -1;
+    cli->ambiguous = 0;
+
+    /* Get the next argument or option */
+    if (!next_argument(clp, cli->option_processing ? 0 : 2)) {
+       clp->val.s = clp->vstr;
+       optno = clp->have_val ? Clp_NotOption : Clp_Done;
+       clp->option = &clp_option_sentinel[-optno];
+       return optno;
+    }
+
+    clp->negated = cli->whole_negated;
+    if (cli->is_short)
+       optno = find_short(clp, cli->xtext);
+    else
+       optno = find_long(clp, cli->xtext);
+
+    /* If there's ambiguity between long & short options, and we couldn't
+       find a long option, look for a short option */
+    if (optno < 0 && cli->could_be_short) {
+       switch_to_short_argument(clp);
+       optno = find_short(clp, cli->xtext);
+    }
+
+    /* If we didn't find an option... */
+    if (optno < 0 || (clp->negated && !cli->iopt[optno].ineg)) {
+       /* default processing for the "--" option: turn off option processing
+          and return the next argument */
+       if (strcmp(cli->argv[0], "--") == 0) {
+           Clp_SetOptionProcessing(clp, 0);
+           return Clp_Next(clp);
+       }
+
+       /* otherwise, report some error or other */
+       if (cli->ambiguous)
+           ambiguity_error(clp, cli->ambiguous, cli->ambiguous_values,
+                           cli->opt, cli->iopt, cli->option_chars,
+                           "option %<%s%s%> is ambiguous",
+                           cli->option_chars, cli->xtext);
+       else if (cli->is_short && !cli->could_be_short)
+           Clp_OptionError(clp, "unrecognized option %<%s%C%>",
+                           cli->option_chars, cli->xtext);
+       else
+           Clp_OptionError(clp, "unrecognized option %<%s%s%>",
+                           cli->option_chars, cli->xtext);
+
+       clp->option = &clp_option_sentinel[-Clp_BadOption];
+       return Clp_BadOption;
+    }
+
+    /* Set the current option */
+    cli->current_option = optno;
+    cli->current_short = cli->is_short;
+    cli->negated_by_no = clp->negated && !cli->whole_negated;
+
+    /* The no-argument (or should-have-no-argument) case */
+    if (clp->negated
+       || (!cli->iopt[optno].imandatory && !cli->iopt[optno].ioptional)) {
+       if (clp->have_val) {
+           Clp_OptionError(clp, "%<%O%> can%,t take an argument");
+           clp->option = &clp_option_sentinel[-Clp_BadOption];
+           return Clp_BadOption;
+       } else {
+           clp->option = &cli->opt[optno];
+           return cli->opt[optno].option_id;
+       }
+    }
+
+    /* Get an argument if we need one, or if it's optional */
+    /* Sanity-check the argument type. */
+    opt = &cli->opt[optno];
+    if (opt->val_type <= 0) {
+       clp->option = &clp_option_sentinel[-Clp_Error];
+       return Clp_Error;
+    }
+    vtpos = val_type_binsearch(cli, opt->val_type);
+    if (vtpos == cli->nvaltype
+       || cli->valtype[vtpos].val_type != opt->val_type) {
+       clp->option = &clp_option_sentinel[-Clp_Error];
+       return Clp_Error;
+    }
+
+    /* complain == 1 only if the argument was explicitly given,
+       or it is mandatory. */
+    complain = (clp->have_val != 0) || cli->iopt[optno].imandatory;
+    Clp_SaveParser(clp, &clpsave);
+
+    if (cli->iopt[optno].imandatory && !clp->have_val) {
+       /* Mandatory argument case */
+       /* Allow arguments to options to start with a dash, but only if the
+          argument type allows it by not setting Clp_DisallowOptions */
+       int disallow = (cli->valtype[vtpos].flags & Clp_DisallowOptions) != 0;
+       next_argument(clp, disallow ? 1 : 2);
+       if (!clp->have_val) {
+           int got_option = cli->xtext != 0;
+           Clp_RestoreParser(clp, &clpsave);
+           if (got_option)
+               Clp_OptionError(clp, "%<%O%> requires a non-option argument");
+           else
+               Clp_OptionError(clp, "%<%O%> requires an argument");
+           clp->option = &clp_option_sentinel[-Clp_BadOption];
+           return Clp_BadOption;
+       }
+
+    } else if (cli->is_short && !clp->have_val
+              && cli->xtext[clp_utf8_charlen(cli, cli->xtext)])
+       /* The -[option]argument case:
+          Assume that the rest of the current string is the argument. */
+       next_argument(clp, 1);
+
+    /* Parse the argument */
+    clp->option = opt;
+    if (clp->have_val) {
+       Clp_ValType *atr = &cli->valtype[vtpos];
+       if (atr->func(clp, clp->vstr, complain, atr->user_data) <= 0) {
+           /* parser failed */
+           clp->have_val = 0;
+           if (complain) {
+               clp->option = &clp_option_sentinel[-Clp_BadOption];
+               return Clp_BadOption;
+           } else {
+               Clp_RestoreParser(clp, &clpsave);
+                clp->option = opt;
+            }
+       }
+    }
+
+    return opt->option_id;
+}
+
+
+/** @param clp the parser
+ * @param allow_options whether options will be allowed
+ *
+ * Remove and return the next argument from @a clp's argument array.  If there
+ * are no arguments left, or if the next argument is an option and @a
+ * allow_options != 0, then returns null.
+ */
+const char *
+Clp_Shift(Clp_Parser *clp, int allow_options)
+     /* Returns the next argument from the argument list without parsing it.
+        If there are no more arguments, returns 0. */
+{
+    Clp_ParserState clpsave;
+    Clp_SaveParser(clp, &clpsave);
+    next_argument(clp, allow_options ? 2 : 1);
+    if (!clp->have_val)
+       Clp_RestoreParser(clp, &clpsave);
+    return clp->vstr;
+}
+
+
+/*******
+ * Clp_OptionError
+ **/
+
+typedef struct Clp_BuildString {
+    char* data;
+    char* pos;
+    char* end_data;
+    char buf[256];
+} Clp_BuildString;
+
+static void build_string_program_prefix(Clp_BuildString* bs,
+                                        const Clp_Parser* clp);
+
+static void build_string_init(Clp_BuildString* bs, Clp_Parser* clp) {
+    bs->data = bs->pos = bs->buf;
+    bs->end_data = &bs->buf[sizeof(bs->buf)];
+    if (clp)
+        build_string_program_prefix(bs, clp);
+}
+
+static void build_string_cleanup(Clp_BuildString* bs) {
+    if (bs->data != bs->buf)
+        free(bs->data);
+}
+
+static int build_string_grow(Clp_BuildString* bs, size_t want) {
+    size_t ipos = bs->pos - bs->data, ncap;
+    if (!bs->pos)
+        return 0;
+    for (ncap = (bs->end_data - bs->data) << 1; ncap < want; ncap *= 2)
+        /* nada */;
+    if (bs->data == bs->buf) {
+        if ((bs->data = (char*) malloc(ncap)))
+            memcpy(bs->data, bs->buf, bs->pos - bs->buf);
+    } else
+        bs->data = (char*) realloc(bs->data, ncap);
+    if (!bs->data) {
+        bs->pos = bs->end_data = bs->data;
+        return 0;
+    } else {
+        bs->pos = bs->data + ipos;
+        bs->end_data = bs->data + ncap;
+        return 1;
+    }
+}
+
+#define ENSURE_BUILD_STRING(bs, space)                                  \
+    ((((bs)->end_data - (bs)->pos) >= (space))                          \
+     || build_string_grow((bs), (bs)->pos - (bs)->data + (space)))
+
+static void
+append_build_string(Clp_BuildString *bs, const char *s, int l)
+{
+    if (l < 0)
+       l = strlen(s);
+    if (ENSURE_BUILD_STRING(bs, l)) {
+       memcpy(bs->pos, s, l);
+       bs->pos += l;
+    }
+}
+
+static void
+build_string_program_prefix(Clp_BuildString* bs, const Clp_Parser* clp)
+{
+    const Clp_Internal* cli = clp->internal;
+    if (cli->program_name && cli->program_name[0]) {
+       append_build_string(bs, cli->program_name, -1);
+       append_build_string(bs, ": ", 2);
+    }
+}
+
+
+static void
+Clp_vbsprintf(Clp_Parser *clp, Clp_BuildString *bs,
+              const char *fmt, va_list val)
+{
+    Clp_Internal *cli = clp->internal;
+    const char *percent;
+    int c;
+
+    for (percent = strchr(fmt, '%'); percent; percent = strchr(fmt, '%')) {
+       append_build_string(bs, fmt, percent - fmt);
+       switch (*++percent) {
+
+         case 's': {
+             const char *s = va_arg(val, const char *);
+              append_build_string(bs, s ? s : "(null)", -1);
+             break;
+         }
+
+         case 'C': {
+             const char *s = va_arg(val, const char *);
+             if (cli->utf8)
+                 c = decode_utf8(s, 0);
+             else
+                 c = (unsigned char) *s;
+             goto char_c;
+         }
+
+         case 'c':
+           c = va_arg(val, int);
+           goto char_c;
+
+         char_c:
+           if (ENSURE_BUILD_STRING(bs, 4)) {
+               if (c >= 32 && c <= 126)
+                   *bs->pos++ = c;
+               else if (c < 32) {
+                   *bs->pos++ = '^';
+                   *bs->pos++ = c + 64;
+               } else if (cli->utf8 && c >= 127 && c < 0x110000) {
+                   bs->pos = encode_utf8(bs->pos, 4, c);
+               } else if (c >= 127 && c <= 255) {
+                   sprintf(bs->pos, "\\%03o", c & 0xFF);
+                   bs->pos += 4;
+               } else {
+                   *bs->pos++ = '\\';
+                   *bs->pos++ = '?';
+               }
+           }
+           break;
+
+         case 'd': {
+             int d = va_arg(val, int);
+             if (ENSURE_BUILD_STRING(bs, 32)) {
+                 sprintf(bs->pos, "%d", d);
+                 bs->pos = strchr(bs->pos, 0);
+             }
+             break;
+         }
+
+        case 'O':
+        case 'V': {
+            int optno = cli->current_option;
+            const Clp_Option *opt = &cli->opt[optno];
+            if (optno < 0)
+                append_build_string(bs, "(no current option!)", -1);
+            else if (cli->current_short) {
+                append_build_string(bs, cli->option_chars, -1);
+                if (ENSURE_BUILD_STRING(bs, 5)) {
+                    if (cli->utf8)
+                        bs->pos = encode_utf8(bs->pos, 5, opt->short_name);
+                    else
+                        *bs->pos++ = opt->short_name;
+                }
+            } else if (cli->negated_by_no) {
+                append_build_string(bs, cli->option_chars, -1);
+                append_build_string(bs, "no-", 3);
+                append_build_string(bs, opt->long_name + cli->iopt[optno].ilongoff, -1);
+            } else {
+                append_build_string(bs, cli->option_chars, -1);
+                append_build_string(bs, opt->long_name + cli->iopt[optno].ilongoff, -1);
+            }
+            if (optno >= 0 && clp->have_val && *percent == 'V') {
+                if (cli->current_short && !cli->iopt[optno].ioptional)
+                    append_build_string(bs, " ", 1);
+                else if (!cli->current_short)
+                    append_build_string(bs, "=", 1);
+                append_build_string(bs, clp->vstr, -1);
+            }
+            break;
+        }
+
+         case '%':
+           if (ENSURE_BUILD_STRING(bs, 1))
+               *bs->pos++ = '%';
+           break;
+
+         case '<':
+           append_build_string(bs, (cli->utf8 ? "\342\200\230" : "'"), -1);
+           break;
+
+         case ',':
+         case '>':
+           append_build_string(bs, (cli->utf8 ? "\342\200\231" : "'"), -1);
+           break;
+
+        case 0:
+            append_build_string(bs, "%", 1);
+            goto done;
+
+         default:
+           if (ENSURE_BUILD_STRING(bs, 2)) {
+               *bs->pos++ = '%';
+               *bs->pos++ = *percent;
+           }
+           break;
+
+       }
+       fmt = ++percent;
+    }
+
+ done:
+    append_build_string(bs, fmt, -1);
+}
+
+static const char* build_string_text(Clp_BuildString* bs, int report_oom) {
+    if (bs->pos) {
+        *bs->pos = 0;
+        return bs->data;
+    } else if (report_oom)
+        return "out of memory\n";
+    else
+        return NULL;
+}
+
+static void
+do_error(Clp_Parser *clp, Clp_BuildString *bs)
+{
+    const char *text = build_string_text(bs, 1);
+    if (clp->internal->error_handler != 0)
+       (*clp->internal->error_handler)(clp, text);
+    else
+       fputs(text, stderr);
+}
+
+/** @param clp the parser
+ * @param format error format
+ *
+ * Format an error message from @a format and any additional arguments in
+ * the ellipsis. The resulting error string is then printed to standard
+ * error (or passed to the error handler specified by Clp_SetErrorHandler).
+ * Returns the number of characters printed.
+ *
+ * The following format characters are accepted:
+ *
+ * <dl>
+ * <dt><tt>%</tt><tt>c</tt></dt>
+ * <dd>A character (type <tt>int</tt>).  Control characters are printed in
+ * caret notation.  If the parser is in UTF-8 mode, the character is formatted
+ * in UTF-8.  Otherwise, special characters are printed with backslashes and
+ * octal notation.</dd>
+ * <dt><tt>%</tt><tt>s</tt></dt>
+ * <dd>A string (type <tt>const char *</tt>).</dd>
+ * <dt><tt>%</tt><tt>C</tt></dt>
+ * <dd>The argument is a string (type <tt>const char *</tt>).  The first
+ * character in this string is printed.  If the parser is in UTF-8 mode, this
+ * may involve multiple bytes.</dd>
+ * <dt><tt>%</tt><tt>d</tt></dt>
+ * <dd>An integer (type <tt>int</tt>).  Printed in decimal.</dd>
+ * <dt><tt>%</tt><tt>O</tt></dt>
+ * <dd>The current option.  No values are read from the argument list; the
+ * current option is defined in the Clp_Parser object itself.</dd>
+ * <dt><tt>%</tt><tt>V</tt></dt>
+ * <dd>Like <tt>%</tt><tt>O</tt>, but also includes the current value,
+ * if any.</dd>
+ * <dt><tt>%%</tt></dt>
+ * <dd>Prints a percent character.</dd>
+ * <dt><tt>%</tt><tt>&lt;</tt></dt>
+ * <dd>Prints an open quote string.  In UTF-8 mode, prints a left single
+ * quote.  Otherwise prints a single quote.</dd>
+ * <dt><tt>%</tt><tt>&gt;</tt></dt>
+ * <dd>Prints a closing quote string.  In UTF-8 mode, prints a right single
+ * quote.  Otherwise prints a single quote.</dd>
+ * <dt><tt>%</tt><tt>,</tt></dt>
+ * <dd>Prints an apostrophe.  In UTF-8 mode, prints a right single quote.
+ * Otherwise prints a single quote.</dd>
+ * </dl>
+ *
+ * Note that no flag characters, precision, or field width characters are
+ * currently supported.
+ *
+ * @sa Clp_SetErrorHandler
+ */
+int
+Clp_OptionError(Clp_Parser *clp, const char *format, ...)
+{
+    Clp_BuildString bs;
+    va_list val;
+    va_start(val, format);
+    build_string_init(&bs, clp);
+    Clp_vbsprintf(clp, &bs, format, val);
+    append_build_string(&bs, "\n", 1);
+    va_end(val);
+    do_error(clp, &bs);
+    build_string_cleanup(&bs);
+    return bs.pos - bs.data;
+}
+
+/** @param clp the parser
+ * @param f output file
+ * @param format error format
+ *
+ * Format an error message using @a format and additional arguments in the
+ * ellipsis, according to the Clp_OptionError formatting conventions. The
+ * resulting message is written to @a f.
+ *
+ * @sa Clp_OptionError */
+int
+Clp_fprintf(Clp_Parser* clp, FILE* f, const char* format, ...)
+{
+    Clp_BuildString bs;
+    va_list val;
+    va_start(val, format);
+    build_string_init(&bs, NULL);
+    Clp_vbsprintf(clp, &bs, format, val);
+    va_end(val);
+    if (bs.pos != bs.data)
+        fwrite(bs.data, 1, bs.pos - bs.data, f);
+    build_string_cleanup(&bs);
+    return bs.pos - bs.data;
+}
+
+/** @param clp the parser
+ * @param f output file
+ * @param format error format
+ * @param val arguments
+ *
+ * Format an error message using @a format and @a val, according to the
+ * Clp_OptionError formatting conventions. The resulting message is written
+ * to @a f.
+ *
+ * @sa Clp_OptionError */
+int
+Clp_vfprintf(Clp_Parser* clp, FILE* f, const char* format, va_list val)
+{
+    Clp_BuildString bs;
+    build_string_init(&bs, NULL);
+    Clp_vbsprintf(clp, &bs, format, val);
+    if (bs.pos != bs.data)
+        fwrite(bs.data, 1, bs.pos - bs.data, f);
+    build_string_cleanup(&bs);
+    return bs.pos - bs.data;
+}
+
+/** @param clp the parser
+ * @param str output string
+ * @param size size of output string
+ * @param format error format
+ *
+ * Format an error message from @a format and any additional arguments in
+ * the ellipsis, according to the Clp_OptionError formatting conventions.
+ * The resulting string is written to @a str. At most @a size characters are
+ * written to @a str, including a terminating null byte. The return value is
+ * the number of characters that would have been written (excluding the
+ * terminating null byte) if @a size were large enough to contain the entire
+ * string.
+ *
+ * @sa Clp_OptionError */
+int
+Clp_vsnprintf(Clp_Parser* clp, char* str, size_t size,
+              const char* format, va_list val)
+{
+    Clp_BuildString bs;
+    build_string_init(&bs, NULL);
+    Clp_vbsprintf(clp, &bs, format, val);
+    if ((size_t) (bs.pos - bs.data) < size) {
+        memcpy(str, bs.data, bs.pos - bs.data);
+        str[bs.pos - bs.data] = 0;
+    } else {
+        memcpy(str, bs.data, size - 1);
+        str[size - 1] = 0;
+    }
+    build_string_cleanup(&bs);
+    return bs.pos - bs.data;
+}
+
+static int
+ambiguity_error(Clp_Parser *clp, int ambiguous, int *ambiguous_values,
+               const Clp_Option *opt, const Clp_InternOption *iopt,
+               const char *prefix, const char *fmt, ...)
+{
+    Clp_Internal *cli = clp->internal;
+    Clp_BuildString bs;
+    int i;
+    va_list val;
+
+    va_start(val, fmt);
+    build_string_init(&bs, clp);
+    Clp_vbsprintf(clp, &bs, fmt, val);
+    append_build_string(&bs, "\n", 1);
+
+    build_string_program_prefix(&bs, clp);
+    append_build_string(&bs, "(Possibilities are", -1);
+
+    for (i = 0; i < ambiguous && i < MAX_AMBIGUOUS_VALUES; i++) {
+       int value = ambiguous_values[i];
+       const char *no_dash = "";
+       if (value < 0)
+           value = -(value + 1), no_dash = "no-";
+       if (i == 0)
+           append_build_string(&bs, " ", 1);
+       else if (i == ambiguous - 1)
+           append_build_string(&bs, (i == 1 ? " and " : ", and "), -1);
+       else
+           append_build_string(&bs, ", ", 2);
+       append_build_string(&bs, (cli->utf8 ? "\342\200\230" : "'"), -1);
+       append_build_string(&bs, prefix, -1);
+       append_build_string(&bs, no_dash, -1);
+       append_build_string(&bs, opt[value].long_name + iopt[value].ilongoff, -1);
+       append_build_string(&bs, (cli->utf8 ? "\342\200\231" : "'"), -1);
+    }
+
+    if (ambiguous > MAX_AMBIGUOUS_VALUES)
+       append_build_string(&bs, ", and others", -1);
+    append_build_string(&bs, ".)\n", -1);
+    va_end(val);
+
+    do_error(clp, &bs);
+    build_string_cleanup(&bs);
+    return 0;
+}
+
+static int
+copy_string(char *buf, int buflen, int bufpos, const char *what)
+{
+    int l = strlen(what);
+    if (l > buflen - bufpos - 1)
+       l = buflen - bufpos - 1;
+    memcpy(buf + bufpos, what, l);
+    return l;
+}
+
+/** @param clp the parser
+ * @param buf output buffer
+ * @param len length of output buffer
+ * @return number of characters written to the buffer, not including the terminating NUL
+ *
+ * A string that looks like the last option parsed by @a clp is extracted into
+ * @a buf.  The correct option characters are put into the string first,
+ * followed by the option text.  The output buffer is null-terminated unless
+ * @a len == 0.
+ *
+ * @sa Clp_CurOptionName
+ */
+int
+Clp_CurOptionNameBuf(Clp_Parser *clp, char *buf, int len)
+{
+    Clp_Internal *cli = clp->internal;
+    int optno = cli->current_option;
+    int pos = 0;
+    if (optno < 0)
+       pos += copy_string(buf, len, pos, "(no current option!)");
+    else if (cli->current_short) {
+       pos += copy_string(buf, len, pos, cli->option_chars);
+       if (cli->utf8)
+           pos = (encode_utf8(buf + pos, len - pos - 1, cli->opt[optno].short_name) - buf);
+       else if (pos < len - 1)
+           buf[pos++] = cli->opt[optno].short_name;
+    } else if (cli->negated_by_no) {
+       pos += copy_string(buf, len, pos, cli->option_chars);
+       pos += copy_string(buf, len, pos, "no-");
+       pos += copy_string(buf, len, pos, cli->opt[optno].long_name + cli->iopt[optno].ilongoff);
+    } else {
+       pos += copy_string(buf, len, pos, cli->option_chars);
+       pos += copy_string(buf, len, pos, cli->opt[optno].long_name + cli->iopt[optno].ilongoff);
+    }
+    if (pos < len)
+       buf[pos] = 0;
+    return pos;
+}
+
+/** @param clp the parser
+ * @return string describing the current option
+ *
+ * This function acts like Clp_CurOptionNameBuf(), but returns a pointer into
+ * a static buffer that will be rewritten on the next call to
+ * Clp_CurOptionName().
+ *
+ * @note This function is not thread safe.
+ *
+ * @sa Clp_CurOptionName
+ */
+const char *
+Clp_CurOptionName(Clp_Parser *clp)
+{
+    static char buf[256];
+    Clp_CurOptionNameBuf(clp, buf, 256);
+    return buf;
+}
+
+int
+Clp_IsLong(Clp_Parser *clp, const char *long_name)
+{
+    Clp_Internal *cli = clp->internal;
+    int optno = cli->current_option;
+    return optno >= 0 && strcmp(cli->opt[optno].long_name, long_name) == 0;
+}
+
+int
+Clp_IsShort(Clp_Parser *clp, int short_name)
+{
+    Clp_Internal *cli = clp->internal;
+    int optno = cli->current_option;
+    return optno >= 0 && cli->opt[optno].short_name == short_name;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/silo/masstree/clp.h b/silo/masstree/clp.h
new file mode 100644 (file)
index 0000000..b355a61
--- /dev/null
@@ -0,0 +1,348 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef LCDF_CLP_H
+#define LCDF_CLP_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* clp.h - Public interface to CLP.
+ * This file is part of CLP, the command line parser package.
+ *
+ * Copyright (c) 1997-2014 Eddie Kohler, ekohler@gmail.com
+ *
+ * CLP is free software. It is distributed under the GNU General Public
+ * License, Version 2, or, alternatively and at your discretion, under the
+ * more permissive (BSD-like) Click LICENSE file as described below.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, subject to the
+ * conditions listed in the Click LICENSE file, which is available in full at
+ * http://github.com/kohler/click/blob/master/LICENSE. The conditions
+ * include: you must preserve this copyright notice, and you cannot mention
+ * the copyright holders in advertising related to the Software without
+ * their permission. The Software is provided WITHOUT ANY WARRANTY, EXPRESS
+ * OR IMPLIED. This notice is a summary of the Click LICENSE file; the
+ * license in that file is binding. */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+typedef struct Clp_Option Clp_Option;
+typedef struct Clp_Parser Clp_Parser;
+typedef struct Clp_ParserState Clp_ParserState;
+
+
+/** @brief Option description.
+ *
+ * CLP users declare arrays of Clp_Option structures to specify what options
+ * should be parsed.
+ * @sa Clp_NewParser, Clp_SetOptions */
+struct Clp_Option {
+    const char *long_name;     /**< Name of long option, or NULL if the option
+                                    has no long name. */
+    int short_name;            /**< Character defining short option, or 0 if
+                                    the option has no short name. */
+    int option_id;             /**< User-specified ID defining option,
+                                    returned by Clp_Next. */
+    int val_type;              /**< ID of option's value type, or 0 if option
+                                    takes no value. */
+    int flags;                 /**< Option parsing flags. */
+};
+
+/** @name Value types
+ * These values describe the type of an option's argument and are used in
+ * the Clp_Option val_type field.  For example, if an option took integers, its
+ * Clp_Option structure would have val_type set to Clp_ValInt. */
+/**@{*/
+#define Clp_NoVal              0       /**< @brief Option takes no value. */
+#define Clp_ValString          1       /**< @brief Option value is an
+                                            arbitrary string. */
+#define Clp_ValStringNotOption 2       /**< @brief Option value is a
+                                            non-option string.
+
+               See Clp_DisallowOptions. */
+#define Clp_ValBool            3       /**< @brief Option value is a
+                                            boolean.
+
+               Accepts "true", "false", "yes", "no", "1", and "0", or any
+               prefixes thereof.  The match is case-insensitive. */
+#define Clp_ValInt             4       /**< @brief Option value is a
+                                            signed int.
+
+               Accepts an optional "+" or "-" sign, followed by one or more
+               digits.  The digits may be include a "0x" or "0X" prefix, for
+               a hexadecimal number, or a "0" prefix, for an octal number;
+               otherwise it is decimal. */
+#define Clp_ValUnsigned                5       /**< @brief Option value is an
+                                            unsigned int.
+
+               Accepts an optional "+" sign, followed by one or more
+               digits.  The digits may be include a "0x" or "0X" prefix, for
+               a hexadecimal number, or a "0" prefix, for an octal number;
+               otherwise it is decimal. */
+#define Clp_ValLong             6       /**< @brief Option value is a
+                                             signed long. */
+#define Clp_ValUnsignedLong     7       /**< @brief Option value is an
+                                             unsigned long. */
+#define Clp_ValDouble          8       /**< @brief Option value is a
+                                            double.
+               Accepts a real number as defined by strtod(). */
+#define Clp_ValFirstUser       10      /**< @brief Value types >=
+                                            Clp_ValFirstUser are available
+                                            for user types. */
+/**@}*/
+
+/** @name Option flags
+ * These flags are used in the Clp_Option flags field. */
+/**@{*/
+#define Clp_Mandatory          (1<<0)  /**< @brief Option flag: value
+                                            is mandatory.
+
+               It is an error if the option has no value.  This is the
+               default if an option has arg_type != 0 and the Clp_Optional
+               flag is not provided. */
+#define Clp_Optional           (1<<1)  /**< @brief Option flag: value
+                                            is optional. */
+#define Clp_Negate             (1<<2)  /**< @brief Option flag: option
+                                            may be negated.
+
+               --no-[long_name] will be accepted in argument lists. */
+#define Clp_OnlyNegated                (1<<3)  /**< @brief Option flag: option
+                                            <em>must</em> be negated.
+
+               --no-[long_name] will be accepted in argument lists, but
+               --[long_name] will not.  This is the default if long_name
+               begins with "no-". */
+#define Clp_PreferredMatch     (1<<4)  /**< @brief Option flag: prefer this
+                                            option when matching.
+
+               Prefixes of --[long_name] should map to this option, even if
+               other options begin with --[long_name]. */
+/**@}*/
+
+/** @name Option character types
+ * These flags are used in to define character types in Clp_SetOptionChar(). */
+/**@{*/
+/*     Clp_NotOption           0 */
+#define Clp_Short              (1<<0)  /**< @brief Option character begins
+                                            a set of short options. */
+#define Clp_Long               (1<<1)  /**< @brief Option character begins
+                                            a long option. */
+#define Clp_ShortNegated       (1<<2)  /**< @brief Option character begins
+                                            a set of negated short options. */
+#define Clp_LongNegated                (1<<3)  /**< @brief Option character begins
+                                            a negated long option. */
+#define Clp_LongImplicit       (1<<4)  /**< @brief Option character can begin
+                                            a long option, and is part of that
+                                            long option. */
+/**@}*/
+
+#define Clp_NotOption          0       /**< @brief Clp_Next value: argument
+                                            was not an option. */
+#define Clp_Done               -1      /**< @brief Clp_Next value: there are
+                                            no more arguments. */
+#define Clp_BadOption          -2      /**< @brief Clp_Next value: argument
+                                            was an erroneous option. */
+#define Clp_Error              -3      /**< @brief Clp_Next value: internal
+                                            CLP error. */
+
+#define Clp_ValSize            40      /**< @brief Minimum size of the
+                                            Clp_Parser val.cs field. */
+#define Clp_ValIntSize         10      /**< @brief Minimum size of the
+                                            Clp_Parser val.is field. */
+
+
+/** @brief A value parsing function.
+ * @param clp the parser
+ * @param vstr the value to be parsed
+ * @param complain if nonzero, report error messages via Clp_OptionError
+ * @param user_data user data passed to Clp_AddType()
+ * @return 1 if parsing succeeded, 0 otherwise
+ */
+typedef int (*Clp_ValParseFunc)(Clp_Parser *clp, const char *vstr,
+                               int complain, void *user_data);
+
+/** @brief A function for reporting option errors.
+ * @param clp the parser
+ * @param message error message
+ */
+typedef void (*Clp_ErrorHandler)(Clp_Parser *clp, const char *message);
+
+
+/** @brief Command line parser.
+ *
+ * A Clp_Parser object defines an instance of CLP, including allowed options,
+ * value types, and current arguments.
+ * @sa Clp_NewParser, Clp_SetOptions, Clp_SetArguments */
+struct Clp_Parser {
+    const Clp_Option *option;  /**< The last option. */
+
+    int negated;               /**< Whether the last option was negated. */
+
+    int have_val;              /**< Whether the last option had a value. */
+    const char *vstr;          /**< The string value provided with the last
+                                    option. */
+
+    union {
+       int i;
+       unsigned u;
+        long l;
+        unsigned long ul;
+       double d;
+       const char *s;
+       void *pv;
+#ifdef HAVE_INT64_TYPES
+       int64_t i64;
+       uint64_t u64;
+#endif
+       char cs[Clp_ValSize];
+       unsigned char ucs[Clp_ValSize];
+       int is[Clp_ValIntSize];
+       unsigned us[Clp_ValIntSize];
+    } val;                     /**< The parsed value provided with the last
+                                    option. */
+
+    void *user_data;           /**< Uninterpreted by CLP; users can set
+                                    arbitrarily. */
+
+    struct Clp_Internal *internal;
+};
+
+/** @cond never */
+#if __GNUC__ >= 4
+# define CLP_SENTINEL          __attribute__((sentinel))
+#else
+# define CLP_SENTINEL          /* nothing */
+#endif
+/** @endcond never */
+
+
+/** @brief Create a new Clp_Parser. */
+Clp_Parser *Clp_NewParser(int argc, const char * const *argv,
+                         int nopt, const Clp_Option *opt);
+
+/** @brief Destroy a Clp_Parser object. */
+void Clp_DeleteParser(Clp_Parser *clp);
+
+
+/** @brief Return @a clp's program name. */
+const char *Clp_ProgramName(Clp_Parser *clp);
+
+/** @brief Set @a clp's program name. */
+const char *Clp_SetProgramName(Clp_Parser *clp, const char *name);
+
+
+/** @brief Set @a clp's error handler function. */
+Clp_ErrorHandler Clp_SetErrorHandler(Clp_Parser *clp, Clp_ErrorHandler errh);
+
+/** @brief Set @a clp's UTF-8 mode. */
+int Clp_SetUTF8(Clp_Parser *clp, int utf8);
+
+/** @brief Return @a clp's treatment of character @a c. */
+int Clp_OptionChar(Clp_Parser *clp, int c);
+
+/** @brief Set @a clp's treatment of character @a c. */
+int Clp_SetOptionChar(Clp_Parser *clp, int c, int type);
+
+/** @brief Set @a clp's option definitions. */
+int Clp_SetOptions(Clp_Parser *clp, int nopt, const Clp_Option *opt);
+
+/** @brief Set @a clp's arguments. */
+void Clp_SetArguments(Clp_Parser *clp, int argc, const char * const *argv);
+
+/** @brief Set whether @a clp is searching for options. */
+int Clp_SetOptionProcessing(Clp_Parser *clp, int on);
+
+
+#define Clp_DisallowOptions    (1<<0)  /**< @brief Value type flag: value
+                                            can't be an option string.
+
+               See Clp_AddType(). */
+
+/** @brief Define a new value type for @a clp. */
+int Clp_AddType(Clp_Parser *clp, int val_type, int flags,
+               Clp_ValParseFunc parser, void *user_data);
+
+
+#define Clp_AllowNumbers       (1<<0)  /**< @brief String list flag: allow
+                                            explicit numbers.
+
+               See Clp_AddStringListType() and Clp_AddStringListTypeVec(). */
+#define Clp_StringListLong      (1<<1)  /**< @brief String list flag: values
+                                             have long type. */
+
+/** @brief Define a new string list value type for @a clp. */
+int Clp_AddStringListTypeVec(Clp_Parser *clp, int val_type, int flags,
+                            int nstrs, const char * const *strs,
+                            const int *vals);
+
+/** @brief Define a new string list value type for @a clp. */
+int Clp_AddStringListType(Clp_Parser *clp, int val_type, int flags, ...)
+                         CLP_SENTINEL;
+
+
+/** @brief Parse and return the next argument from @a clp. */
+int Clp_Next(Clp_Parser *clp);
+
+/** @brief Return the next argument from @a clp without option parsing. */
+const char *Clp_Shift(Clp_Parser *clp, int allow_options);
+
+
+/** @brief Create a new Clp_ParserState. */
+Clp_ParserState *Clp_NewParserState(void);
+
+/** @brief Destroy a Clp_ParserState object. */
+void Clp_DeleteParserState(Clp_ParserState *state);
+
+/** @brief Save @a clp's current state in @a state. */
+void Clp_SaveParser(const Clp_Parser *clp, Clp_ParserState *state);
+
+/** @brief Restore parser state from @a state into @a clp. */
+void Clp_RestoreParser(Clp_Parser *clp, const Clp_ParserState *state);
+
+
+/** @brief Report a parser error. */
+int Clp_OptionError(Clp_Parser *clp, const char *format, ...);
+
+/** @brief Format a message. */
+int Clp_vsnprintf(Clp_Parser* clp, char* str, size_t size,
+                  const char* format, va_list val);
+
+/** @brief Print a message. */
+int Clp_fprintf(Clp_Parser* clp, FILE* f, const char* format, ...);
+
+/** @brief Print a message. */
+int Clp_vfprintf(Clp_Parser* clp, FILE* f, const char* format, va_list val);
+
+/** @brief Extract the current option as a string. */
+int Clp_CurOptionNameBuf(Clp_Parser *clp, char *buf, int len);
+
+/** @brief Extract the current option as a string. */
+const char *Clp_CurOptionName(Clp_Parser *clp);
+
+/** @brief Test if the current option had long name @a name. */
+int Clp_IsLong(Clp_Parser *clp, const char *long_name);
+
+/** @brief Test if the current option had short name @a name. */
+int Clp_IsShort(Clp_Parser *clp, int short_name);
+
+#undef CLP_SENTINEL
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/silo/masstree/compiler.cc b/silo/masstree/compiler.cc
new file mode 100644 (file)
index 0000000..fbb1251
--- /dev/null
@@ -0,0 +1,51 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "compiler.hh"
+#include <stdio.h>
+#include <stdlib.h>
+
+void fail_always_assert(const char* file, int line,
+                        const char* assertion, const char* message) {
+    if (message)
+        fprintf(stderr, "assertion \"%s\" [%s] failed: file \"%s\", line %d\n",
+                message, assertion, file, line);
+    else
+        fprintf(stderr, "assertion \"%s\" failed: file \"%s\", line %d\n",
+                assertion, file, line);
+    abort();
+}
+
+void fail_masstree_invariant(const char* file, int line,
+                             const char* assertion, const char* message) {
+    if (message)
+        fprintf(stderr, "invariant \"%s\" [%s] failed: file \"%s\", line %d\n",
+                message, assertion, file, line);
+    else
+        fprintf(stderr, "invariant \"%s\" failed: file \"%s\", line %d\n",
+                assertion, file, line);
+    abort();
+}
+
+void fail_masstree_precondition(const char* file, int line,
+                                const char* assertion, const char* message) {
+    if (message)
+        fprintf(stderr, "precondition \"%s\" [%s] failed: file \"%s\", line %d\n",
+                message, assertion, file, line);
+    else
+        fprintf(stderr, "precondition \"%s\" failed: file \"%s\", line %d\n",
+                assertion, file, line);
+    abort();
+}
diff --git a/silo/masstree/compiler.hh b/silo/masstree/compiler.hh
new file mode 100644 (file)
index 0000000..03aa077
--- /dev/null
@@ -0,0 +1,1178 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_COMPILER_HH
+#define MASSTREE_COMPILER_HH 1
+#include <stdint.h>
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <arpa/inet.h>
+#if HAVE_TYPE_TRAITS
+#include <type_traits>
+#endif
+
+#define arraysize(a) (sizeof(a) / sizeof((a)[0]))
+
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+#if !HAVE_CXX_STATIC_ASSERT
+#define static_assert(x, msg) switch (x) case 0: case !!(x):
+#endif
+
+#if !HAVE_CXX_CONSTEXPR
+#define constexpr const
+#endif
+
+#if HAVE_OFF_T_IS_LONG_LONG
+#define PRIdOFF_T "lld"
+#else
+#define PRIdOFF_T "ld"
+#endif
+
+#if HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG
+#define PRIdSIZE_T "llu"
+#define PRIdSSIZE_T "lld"
+#elif HAVE_SIZE_T_IS_UNSIGNED_LONG
+#define PRIdSIZE_T "lu"
+#define PRIdSSIZE_T "ld"
+#else
+#define PRIdSIZE_T "u"
+#define PRIdSSIZE_T "d"
+#endif
+
+#if (__i386__ || __x86_64__) && !defined(__x86__)
+# define __x86__ 1
+#endif
+#define PREFER_X86 1
+#define ALLOW___SYNC_BUILTINS 1
+
+#if !defined(HAVE_INDIFFERENT_ALIGMENT) && (__i386__ || __x86_64__ || __arch_um__)
+# define HAVE_INDIFFERENT_ALIGNMENT 1
+#endif
+
+
+/** @brief Return the index of the most significant bit set in @a x.
+ * @return 0 if @a x = 0; otherwise the index of first bit set, where the
+ * most significant bit is numbered 1.
+ */
+inline int ffs_msb(unsigned x) {
+    return (x ? __builtin_clz(x) + 1 : 0);
+}
+/** @overload */
+inline int ffs_msb(unsigned long x) {
+    return (x ? __builtin_clzl(x) + 1 : 0);
+}
+/** @overload */
+inline int ffs_msb(unsigned long long x) {
+    return (x ? __builtin_clzll(x) + 1 : 0);
+}
+
+
+/** @brief Compiler fence.
+ *
+ * Prevents reordering of loads and stores by the compiler. Not intended to
+ * synchronize the processor's caches. */
+inline void fence() {
+    asm volatile("" : : : "memory");
+}
+
+/** @brief Acquire fence. */
+inline void acquire_fence() {
+    asm volatile("" : : : "memory");
+}
+
+/** @brief Release fence. */
+inline void release_fence() {
+    asm volatile("" : : : "memory");
+}
+
+/** @brief Compiler fence that relaxes the processor.
+
+    Use this in spinloops, for example. */
+inline void relax_fence() {
+    asm volatile("pause" : : : "memory"); // equivalent to "rep; nop"
+}
+
+/** @brief Full memory fence. */
+inline void memory_fence() {
+    asm volatile("mfence" : : : "memory");
+}
+
+/** @brief Do-nothing function object. */
+struct do_nothing {
+    void operator()() const {
+    }
+    template <typename T>
+    void operator()(const T&) const {
+    }
+    template <typename T, typename U>
+    void operator()(const T&, const U&) const {
+    }
+};
+
+/** @brief Function object that calls fence(). */
+struct fence_function {
+    void operator()() const {
+        fence();
+    }
+};
+
+/** @brief Function object that calls relax_fence(). */
+struct relax_fence_function {
+    void operator()() const {
+        relax_fence();
+    }
+};
+
+/** @brief Function object that calls relax_fence() with backoff. */
+struct backoff_fence_function {
+    backoff_fence_function()
+        : count_(0) {
+    }
+    void operator()() {
+        for (int i = count_; i >= 0; --i)
+            relax_fence();
+        count_ = ((count_ << 1) | 1) & 15;
+    }
+  private:
+    int count_;
+};
+
+
+template <int SIZE, typename BARRIER> struct sized_compiler_operations;
+
+template <typename B> struct sized_compiler_operations<1, B> {
+    typedef char type;
+    static inline type xchg(type* object, type new_value) {
+        asm volatile("xchgb %0,%1"
+                     : "+q" (new_value), "+m" (*object));
+        B()();
+        return new_value;
+    }
+    static inline type val_cmpxchg(type* object, type expected, type desired) {
+#if __x86__ && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP)
+        asm volatile("lock; cmpxchgb %2,%1"
+                     : "+a" (expected), "+m" (*object)
+                     : "r" (desired) : "cc");
+        B()();
+        return expected;
+#else
+        return __sync_val_compare_and_swap(object, expected, desired);
+#endif
+    }
+    static inline bool bool_cmpxchg(type* object, type expected, type desired) {
+#if HAVE___SYNC_BOOL_COMPARE_AND_SWAP && ALLOW___SYNC_BUILTINS
+        return __sync_bool_compare_and_swap(object, expected, desired);
+#else
+        bool result;
+        asm volatile("lock; cmpxchgb %3,%1; sete %b2"
+                     : "+a" (expected), "+m" (*object), "=q" (result)
+                     : "q" (desired) : "cc");
+        B()();
+        return result;
+#endif
+    }
+    static inline type fetch_and_add(type *object, type addend) {
+#if __x86__ && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD)
+        asm volatile("lock; xaddb %0,%1"
+                     : "+q" (addend), "+m" (*object) : : "cc");
+        B()();
+        return addend;
+#else
+        return __sync_fetch_and_add(object, addend);
+#endif
+    }
+    static inline void atomic_or(type* object, type addend) {
+#if __x86__
+        asm volatile("lock; orb %0,%1"
+                     : "=r" (addend), "+m" (*object) : : "cc");
+        B()();
+#else
+        __sync_fetch_and_or(object, addend);
+#endif
+    }
+};
+
+template <typename B> struct sized_compiler_operations<2, B> {
+#if SIZEOF_SHORT == 2
+    typedef short type;
+#else
+    typedef int16_t type;
+#endif
+    static inline type xchg(type* object, type new_value) {
+        asm volatile("xchgw %0,%1"
+                     : "+r" (new_value), "+m" (*object));
+        B()();
+        return new_value;
+    }
+    static inline type val_cmpxchg(type* object, type expected, type desired) {
+#if __x86__ && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP)
+        asm volatile("lock; cmpxchgw %2,%1"
+                     : "+a" (expected), "+m" (*object)
+                     : "r" (desired) : "cc");
+        B()();
+        return expected;
+#else
+        return __sync_val_compare_and_swap(object, expected, desired);
+#endif
+    }
+    static inline bool bool_cmpxchg(type* object, type expected, type desired) {
+#if HAVE___SYNC_BOOL_COMPARE_AND_SWAP && ALLOW___SYNC_BUILTINS
+        return __sync_bool_compare_and_swap(object, expected, desired);
+#else
+        bool result;
+        asm volatile("lock; cmpxchgw %3,%1; sete %b2"
+                     : "+a" (expected), "+m" (*object), "=q" (result)
+                     : "r" (desired) : "cc");
+        B()();
+        return result;
+#endif
+    }
+    static inline type fetch_and_add(type* object, type addend) {
+#if __x86__ && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD)
+        asm volatile("lock; xaddw %0,%1"
+                     : "+r" (addend), "+m" (*object) : : "cc");
+        B()();
+        return addend;
+#else
+        return __sync_fetch_and_add(object, addend);
+#endif
+    }
+    static inline void atomic_or(type* object, type addend) {
+#if __x86__
+        asm volatile("lock; orw %0,%1"
+                     : "=r" (addend), "+m" (*object) : : "cc");
+        B()();
+#else
+        __sync_fetch_and_or(object, addend);
+#endif
+    }
+};
+
+template <typename B> struct sized_compiler_operations<4, B> {
+#if SIZEOF_INT == 4
+    typedef int type;
+#else
+    typedef int32_t type;
+#endif
+    static inline type xchg(type* object, type new_value) {
+        asm volatile("xchgl %0,%1"
+                     : "+r" (new_value), "+m" (*object));
+        B()();
+        return new_value;
+    }
+    static inline type val_cmpxchg(type* object, type expected, type desired) {
+#if __x86__ && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP)
+        asm volatile("lock; cmpxchgl %2,%1"
+                     : "+a" (expected), "+m" (*object)
+                     : "r" (desired) : "cc");
+        B()();
+        return expected;
+#else
+        return __sync_val_compare_and_swap(object, expected, desired);
+#endif
+    }
+    static inline bool bool_cmpxchg(type* object, type expected, type desired) {
+#if HAVE___SYNC_BOOL_COMPARE_AND_SWAP && ALLOW___SYNC_BUILTINS
+        return __sync_bool_compare_and_swap(object, expected, desired);
+#else
+        bool result;
+        asm volatile("lock; cmpxchgl %3,%1; sete %b2"
+                     : "+a" (expected), "+m" (*object), "=q" (result)
+                     : "r" (desired) : "cc");
+        B()();
+        return result;
+#endif
+    }
+    static inline type fetch_and_add(type *object, type addend) {
+#if __x86__ && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD)
+        asm volatile("lock; xaddl %0,%1"
+                     : "+r" (addend), "+m" (*object) : : "cc");
+        B()();
+        return addend;
+#else
+        return __sync_fetch_and_add(object, addend);
+#endif
+    }
+    static inline void atomic_or(type* object, type addend) {
+#if __x86__
+        asm volatile("lock; orl %0,%1"
+                     : "=r" (addend), "+m" (*object) : : "cc");
+        B()();
+#else
+        __sync_fetch_and_or(object, addend);
+#endif
+    }
+};
+
+template <typename B> struct sized_compiler_operations<8, B> {
+#if SIZEOF_LONG_LONG == 8
+    typedef long long type;
+#elif SIZEOF_LONG == 8
+    typedef long type;
+#else
+    typedef int64_t type;
+#endif
+#if __x86_64__
+    static inline type xchg(type* object, type new_value) {
+        asm volatile("xchgq %0,%1"
+                     : "+r" (new_value), "+m" (*object));
+        B()();
+        return new_value;
+    }
+#endif
+    static inline type val_cmpxchg(type* object, type expected, type desired) {
+#if __x86_64__ && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP_8)
+        asm volatile("lock; cmpxchgq %2,%1"
+                     : "+a" (expected), "+m" (*object)
+                     : "r" (desired) : "cc");
+        B()();
+        return expected;
+#elif __i386__ && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP_8)
+        uint32_t expected_low(expected), expected_high(expected >> 32),
+            desired_low(desired), desired_high(desired >> 32);
+        asm volatile("lock; cmpxchg8b %2"
+                     : "+a" (expected_low), "+d" (expected_high), "+m" (*object)
+                     : "b" (desired_low), "c" (desired_high) : "cc");
+        B()();
+        return ((uint64_t) expected_high << 32) | expected_low;
+#elif HAVE___SYNC_VAL_COMPARE_AND_SWAP_8
+        return __sync_val_compare_and_swap(object, expected, desired);
+#endif
+    }
+    static inline bool bool_cmpxchg(type* object, type expected, type desired) {
+#if HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 && ALLOW___SYNC_BUILTINS
+        return __sync_bool_compare_and_swap(object, expected, desired);
+#elif __x86_64__
+        bool result;
+        asm volatile("lock; cmpxchgq %3,%1; sete %b2"
+                     : "+a" (expected), "+m" (*object), "=q" (result)
+                     : "r" (desired) : "cc");
+        B()();
+        return result;
+#else
+        uint32_t expected_low(expected), expected_high(expected >> 32),
+            desired_low(desired), desired_high(desired >> 32);
+        bool result;
+        asm volatile("lock; cmpxchg8b %2; sete %b4"
+                     : "+a" (expected_low), "+d" (expected_high),
+                       "+m" (*object), "=q" (result)
+                     : "b" (desired_low), "c" (desired_high) : "cc");
+        B()();
+        return result;
+#endif
+    }
+#if __x86_64__ || HAVE___SYNC_FETCH_AND_ADD_8
+    static inline type fetch_and_add(type* object, type addend) {
+# if __x86_64__ && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD_8)
+        asm volatile("lock; xaddq %0,%1"
+                     : "+r" (addend), "+m" (*object) : : "cc");
+        B()();
+        return addend;
+# else
+        return __sync_fetch_and_add(object, addend);
+# endif
+    }
+#endif
+#if __x86_64__ || HAVE___SYNC_FETCH_AND_OR_8
+    static inline void atomic_or(type* object, type addend) {
+#if __x86_64__
+        asm volatile("lock; orq %0,%1"
+                     : "=r" (addend), "+m" (*object) : : "cc");
+        B()();
+#else
+        __sync_fetch_and_or(object, addend);
+#endif
+    }
+#endif
+};
+
+template<typename T>
+inline T xchg(T* object, T new_value) {
+    typedef sized_compiler_operations<sizeof(T), fence_function> sco_t;
+    typedef typename sco_t::type type;
+    return (T) sco_t::xchg((type*) object, (type) new_value);
+}
+
+inline int8_t xchg(int8_t* object, int new_value) {
+    return xchg(object, (int8_t) new_value);
+}
+inline uint8_t xchg(uint8_t* object, int new_value) {
+    return xchg(object, (uint8_t) new_value);
+}
+inline int16_t xchg(int16_t* object, int new_value) {
+    return xchg(object, (int16_t) new_value);
+}
+inline uint16_t xchg(uint16_t* object, int new_value) {
+    return xchg(object, (uint16_t) new_value);
+}
+inline unsigned xchg(unsigned* object, int new_value) {
+    return xchg(object, (unsigned) new_value);
+}
+
+/** @brief Atomic compare and exchange. Return actual old value.
+ * @param object pointer to memory value
+ * @param expected old value
+ * @param desired new value
+ * @return actual old value
+ *
+ * Acts like an atomic version of:
+ * @code
+ * T actual(*object);
+ * if (actual == expected)
+ *    *object = desired;
+ * return actual;
+ * @endcode */
+template <typename T>
+inline T cmpxchg(T* object, T expected, T desired) {
+    typedef sized_compiler_operations<sizeof(T), fence_function> sco_t;
+    typedef typename sco_t::type type;
+    return (T) sco_t::val_cmpxchg((type*) object, (type) expected, (type) desired);
+}
+
+inline unsigned cmpxchg(unsigned *object, int expected, int desired) {
+    return cmpxchg(object, unsigned(expected), unsigned(desired));
+}
+
+/** @brief Atomic compare and exchange. Return true iff swap succeeds.
+ * @param object pointer to memory value
+ * @param expected old value
+ * @param desired new value
+ * @return true if swap succeeded, false otherwise
+ *
+ * Acts like an atomic version of:
+ * @code
+ * T actual(*object);
+ * if (actual == expected) {
+ *    *object = desired;
+ *    return true;
+ * } else
+ *    return false;
+ * @endcode */
+template <typename T>
+inline bool bool_cmpxchg(T* object, T expected, T desired) {
+    typedef sized_compiler_operations<sizeof(T), fence_function> sco_t;
+    typedef typename sco_t::type type;
+    return sco_t::bool_cmpxchg((type*) object, (type) expected, (type) desired);
+}
+
+inline bool bool_cmpxchg(uint8_t* object, int expected, int desired) {
+    return bool_cmpxchg(object, uint8_t(expected), uint8_t(desired));
+}
+inline bool bool_cmpxchg(unsigned *object, int expected, int desired) {
+    return bool_cmpxchg(object, unsigned(expected), unsigned(desired));
+}
+
+/** @brief Atomic fetch-and-add. Return the old value.
+ * @param object pointer to integer
+ * @param addend value to add
+ * @return old value */
+template <typename T>
+inline T fetch_and_add(T* object, T addend) {
+    typedef sized_compiler_operations<sizeof(T), fence_function> sco_t;
+    typedef typename sco_t::type type;
+    return (T) sco_t::fetch_and_add((type*) object, (type) addend);
+}
+
+template <typename T>
+inline T* fetch_and_add(T** object, int addend) {
+    typedef sized_compiler_operations<sizeof(T*), fence_function> sco_t;
+    typedef typename sco_t::type type;
+    return (T*) sco_t::fetch_and_add((type*) object, (type) (addend * sizeof(T)));
+}
+
+inline int8_t fetch_and_add(int8_t* object, int addend) {
+    return fetch_and_add(object, int8_t(addend));
+}
+inline uint8_t fetch_and_add(uint8_t* object, int addend) {
+    return fetch_and_add(object, uint8_t(addend));
+}
+inline int16_t fetch_and_add(int16_t* object, int addend) {
+    return fetch_and_add(object, int16_t(addend));
+}
+inline uint16_t fetch_and_add(uint16_t* object, int addend) {
+    return fetch_and_add(object, uint16_t(addend));
+}
+inline unsigned fetch_and_add(unsigned* object, int addend) {
+    return fetch_and_add(object, unsigned(addend));
+}
+inline unsigned long fetch_and_add(unsigned long* object, int addend) {
+    return fetch_and_add(object, (unsigned long)(addend));
+}
+
+
+/** @brief Test-and-set lock acquire. */
+template <typename T>
+inline void test_and_set_acquire(T* object) {
+    typedef sized_compiler_operations<sizeof(T), do_nothing> sco_t;
+    typedef typename sco_t::type type;
+    while (sco_t::xchg((type*) object, (type) 1))
+        relax_fence();
+    acquire_fence();
+}
+
+/** @brief Test-and-set lock release. */
+template <typename T>
+inline void test_and_set_release(T* object) {
+    release_fence();
+    *object = T();
+}
+
+
+/** @brief Atomic fetch-and-or. Returns nothing.
+ * @param object pointer to integer
+ * @param addend value to or */
+template <typename T>
+inline void atomic_or(T* object, T addend) {
+    typedef sized_compiler_operations<sizeof(T), fence_function> sco_t;
+    typedef typename sco_t::type type;
+    sco_t::atomic_or((type*) object, (type) addend);
+}
+
+inline void atomic_or(int8_t* object, int addend) {
+    atomic_or(object, int8_t(addend));
+}
+inline void atomic_or(uint8_t* object, int addend) {
+    atomic_or(object, uint8_t(addend));
+}
+inline void atomic_or(int16_t* object, int addend) {
+    atomic_or(object, int16_t(addend));
+}
+inline void atomic_or(uint16_t* object, int addend) {
+    atomic_or(object, uint16_t(addend));
+}
+inline void atomic_or(unsigned* object, int addend) {
+    atomic_or(object, unsigned(addend));
+}
+inline void atomic_or(unsigned long* object, int addend) {
+    atomic_or(object, (unsigned long)(addend));
+}
+
+
+// prefetch instruction
+#if !PREFETCH_DEFINED
+inline void prefetch(const void *ptr) {
+#ifdef NOPREFETCH
+    (void) ptr;
+#else
+    typedef struct { char x[CACHE_LINE_SIZE]; } cacheline_t;
+    asm volatile("prefetcht0 %0" : : "m" (*(const cacheline_t *)ptr));
+#endif
+}
+#endif
+
+inline void prefetchnta(const void *ptr) {
+#ifdef NOPREFETCH
+    (void) ptr;
+#else
+    typedef struct { char x[CACHE_LINE_SIZE]; } cacheline_t;
+    asm volatile("prefetchnta %0" : : "m" (*(const cacheline_t *)ptr));
+#endif
+}
+
+
+template <typename T>
+struct value_prefetcher {
+    void operator()(T) {
+    }
+};
+
+template <typename T>
+struct value_prefetcher<T *> {
+    void operator()(T *p) {
+        prefetch((const void *) p);
+    }
+};
+
+
+// stolen from Linux
+inline uint64_t ntohq(uint64_t val) {
+#ifdef __i386__
+    union {
+        struct {
+            uint32_t a;
+            uint32_t b;
+        } s;
+        uint64_t u;
+    } v;
+    v.u = val;
+    asm("bswapl %0; bswapl %1; xchgl %0,%1"
+        : "+r" (v.s.a), "+r" (v.s.b));
+    return v.u;
+#else /* __i386__ */
+    asm("bswapq %0" : "+r" (val));
+    return val;
+#endif
+}
+
+inline uint64_t htonq(uint64_t val) {
+    return ntohq(val);
+}
+
+
+/** Bit counting. */
+
+/** @brief Return the number of leading 0 bits in @a x.
+ * @pre @a x != 0
+ *
+ * "Leading" means "most significant." */
+#if HAVE___BUILTIN_CLZ
+inline int clz(int x) {
+    return __builtin_clz(x);
+}
+inline int clz(unsigned x) {
+    return __builtin_clz(x);
+}
+#endif
+
+#if HAVE___BUILTIN_CLZL
+inline int clz(long x) {
+    return __builtin_clzl(x);
+}
+inline int clz(unsigned long x) {
+    return __builtin_clzl(x);
+}
+#endif
+
+#if HAVE___BUILTIN_CLZLL
+inline int clz(long long x) {
+    return __builtin_clzll(x);
+}
+inline int clz(unsigned long long x) {
+    return __builtin_clzll(x);
+}
+#endif
+
+/** @brief Return the number of trailing 0 bits in @a x.
+ * @pre @a x != 0
+ *
+ * "Trailing" means "least significant." */
+#if HAVE___BUILTIN_CTZ
+inline int ctz(int x) {
+    return __builtin_ctz(x);
+}
+inline int ctz(unsigned x) {
+    return __builtin_ctz(x);
+}
+#endif
+
+#if HAVE___BUILTIN_CTZL
+inline int ctz(long x) {
+    return __builtin_ctzl(x);
+}
+inline int ctz(unsigned long x) {
+    return __builtin_ctzl(x);
+}
+#endif
+
+#if HAVE___BUILTIN_CTZLL
+inline int ctz(long long x) {
+    return __builtin_ctzll(x);
+}
+inline int ctz(unsigned long long x) {
+    return __builtin_ctzll(x);
+}
+#endif
+
+template <typename T, typename U>
+inline T iceil(T x, U y) {
+    U mod = x % y;
+    return x + (mod ? y - mod : 0);
+}
+
+/** @brief Return the smallest power of 2 greater than or equal to @a x.
+    @pre @a x != 0
+    @pre the result is representable in type T (that is, @a x can't be
+    larger than the largest power of 2 representable in type T) */
+template <typename T>
+inline T iceil_log2(T x) {
+    return T(1) << (sizeof(T) * 8 - clz(x) - !(x & (x - 1)));
+}
+
+/** @brief Return the largest power of 2 less than or equal to @a x.
+    @pre @a x != 0 */
+template <typename T>
+inline T ifloor_log2(T x) {
+    return T(1) << (sizeof(T) * 8 - 1 - clz(x));
+}
+
+/** @brief Return the index of the lowest 0 nibble in @a x.
+ *
+ * 0 is the lowest-order nibble. Returns -1 if no nibbles are 0. */
+template <typename T>
+inline int find_lowest_zero_nibble(T x) {
+    static_assert(sizeof(T) <= sizeof(unsigned long long), "T is too big");
+#if SIZEOF_LONG_LONG == 16
+    T h = T(0x88888888888888888888888888888888ULL), l = T(0x11111111111111111111111111111111ULL);
+#else
+    T h = T(0x8888888888888888ULL), l = T(0x1111111111111111ULL);
+#endif
+    T t = h & (x - l) & ~x;
+    return t ? ctz(t) >> 2 : -1;
+}
+
+/** @brief Translate @a x to network byte order.
+ *
+ * Compare htons/htonl/htonq.  host_to_net_order is particularly useful in
+ * template functions, where the type to be translated to network byte order
+ * is unknown. */
+inline unsigned char host_to_net_order(unsigned char x) {
+    return x;
+}
+/** @overload */
+inline signed char host_to_net_order(signed char x) {
+    return x;
+}
+/** @overload */
+inline char host_to_net_order(char x) {
+    return x;
+}
+/** @overload */
+inline short host_to_net_order(short x) {
+    return htons(x);
+}
+/** @overload */
+inline unsigned short host_to_net_order(unsigned short x) {
+    return htons(x);
+}
+/** @overload */
+inline int host_to_net_order(int x) {
+    return htonl(x);
+}
+/** @overload */
+inline unsigned host_to_net_order(unsigned x) {
+    return htonl(x);
+}
+#if SIZEOF_LONG == 4
+/** @overload */
+inline long host_to_net_order(long x) {
+    return htonl(x);
+}
+/** @overload */
+inline unsigned long host_to_net_order(unsigned long x) {
+    return htonl(x);
+}
+#elif SIZEOF_LONG == 8
+/** @overload */
+inline long host_to_net_order(long x) {
+    return htonq(x);
+}
+/** @overload */
+inline unsigned long host_to_net_order(unsigned long x) {
+    return htonq(x);
+}
+#endif
+#if SIZEOF_LONG_LONG == 8
+/** @overload */
+inline long long host_to_net_order(long long x) {
+    return htonq(x);
+}
+/** @overload */
+inline unsigned long long host_to_net_order(unsigned long long x) {
+    return htonq(x);
+}
+#endif
+#if !HAVE_INT64_T_IS_LONG && !HAVE_INT64_T_IS_LONG_LONG
+/** @overload */
+inline int64_t host_to_net_order(int64_t x) {
+    return htonq(x);
+}
+/** @overload */
+inline uint64_t host_to_net_order(uint64_t x) {
+    return htonq(x);
+}
+#endif
+/** @overload */
+inline double host_to_net_order(float x) {
+    union { float f; uint32_t i; } v;
+    v.f = x;
+    v.i = host_to_net_order(v.i);
+    return v.f;
+}
+/** @overload */
+inline double host_to_net_order(double x) {
+    union { double d; uint64_t i; } v;
+    v.d = x;
+    v.i = host_to_net_order(v.i);
+    return v.d;
+}
+
+/** @brief Translate @a x to host byte order.
+ *
+ * Compare ntohs/ntohl/ntohq.  net_to_host_order is particularly useful in
+ * template functions, where the type to be translated to network byte order
+ * is unknown. */
+inline unsigned char net_to_host_order(unsigned char x) {
+    return x;
+}
+/** @overload */
+inline signed char net_to_host_order(signed char x) {
+    return x;
+}
+/** @overload */
+inline char net_to_host_order(char x) {
+    return x;
+}
+/** @overload */
+inline short net_to_host_order(short x) {
+    return ntohs(x);
+}
+/** @overload */
+inline unsigned short net_to_host_order(unsigned short x) {
+    return ntohs(x);
+}
+/** @overload */
+inline int net_to_host_order(int x) {
+    return ntohl(x);
+}
+/** @overload */
+inline unsigned net_to_host_order(unsigned x) {
+    return ntohl(x);
+}
+#if SIZEOF_LONG == 4
+/** @overload */
+inline long net_to_host_order(long x) {
+    return ntohl(x);
+}
+/** @overload */
+inline unsigned long net_to_host_order(unsigned long x) {
+    return ntohl(x);
+}
+#elif SIZEOF_LONG == 8
+/** @overload */
+inline long net_to_host_order(long x) {
+    return ntohq(x);
+}
+/** @overload */
+inline unsigned long net_to_host_order(unsigned long x) {
+    return ntohq(x);
+}
+#endif
+#if SIZEOF_LONG_LONG == 8
+/** @overload */
+inline long long net_to_host_order(long long x) {
+    return ntohq(x);
+}
+/** @overload */
+inline unsigned long long net_to_host_order(unsigned long long x) {
+    return ntohq(x);
+}
+#endif
+#if !HAVE_INT64_T_IS_LONG && !HAVE_INT64_T_IS_LONG_LONG
+/** @overload */
+inline int64_t net_to_host_order(int64_t x) {
+    return ntohq(x);
+}
+/** @overload */
+inline uint64_t net_to_host_order(uint64_t x) {
+    return ntohq(x);
+}
+#endif
+/** @overload */
+inline double net_to_host_order(float x) {
+    return host_to_net_order(x);
+}
+/** @overload */
+inline double net_to_host_order(double x) {
+    return host_to_net_order(x);
+}
+
+template <typename T> struct make_aliasable {};
+#define MAKE_ALIASABLE(T) template <> struct make_aliasable<T> { typedef T type __attribute__((__may_alias__)); }
+MAKE_ALIASABLE(unsigned char);
+MAKE_ALIASABLE(signed char);
+MAKE_ALIASABLE(char);
+MAKE_ALIASABLE(unsigned short);
+MAKE_ALIASABLE(short);
+MAKE_ALIASABLE(int);
+MAKE_ALIASABLE(unsigned);
+MAKE_ALIASABLE(long);
+MAKE_ALIASABLE(unsigned long);
+MAKE_ALIASABLE(long long);
+MAKE_ALIASABLE(unsigned long long);
+MAKE_ALIASABLE(float);
+MAKE_ALIASABLE(double);
+#undef MAKE_ALIASABLE
+
+template <typename T>
+inline char* write_in_host_order(char* s, T x) {
+#if HAVE_INDIFFERENT_ALIGNMENT
+    *reinterpret_cast<typename make_aliasable<T>::type*>(s) = x;
+#else
+    memcpy(s, &x, sizeof(x));
+#endif
+    return s + sizeof(x);
+}
+
+template <typename T>
+inline uint8_t* write_in_host_order(uint8_t* s, T x) {
+    return reinterpret_cast<uint8_t*>
+        (write_in_host_order(reinterpret_cast<char*>(s), x));
+}
+
+template <typename T>
+inline T read_in_host_order(const char* s) {
+#if HAVE_INDIFFERENT_ALIGNMENT
+    return *reinterpret_cast<const typename make_aliasable<T>::type*>(s);
+#else
+    T x;
+    memcpy(&x, s, sizeof(x));
+    return x;
+#endif
+}
+
+template <typename T>
+inline T read_in_host_order(const uint8_t* s) {
+    return read_in_host_order<T>(reinterpret_cast<const char*>(s));
+}
+
+template <typename T>
+inline char* write_in_net_order(char* s, T x) {
+    return write_in_host_order<T>(s, host_to_net_order(x));
+}
+
+template <typename T>
+inline uint8_t* write_in_net_order(uint8_t* s, T x) {
+    return reinterpret_cast<uint8_t*>
+        (write_in_net_order(reinterpret_cast<char*>(s), x));
+}
+
+template <typename T>
+inline T read_in_net_order(const char* s) {
+    return net_to_host_order(read_in_host_order<T>(s));
+}
+
+template <typename T>
+inline T read_in_net_order(const uint8_t* s) {
+    return read_in_net_order<T>(reinterpret_cast<const char*>(s));
+}
+
+
+inline uint64_t read_pmc(uint32_t ecx) {
+    uint32_t a, d;
+    __asm __volatile("rdpmc" : "=a"(a), "=d"(d) : "c"(ecx));
+    return ((uint64_t)a) | (((uint64_t)d) << 32);
+}
+
+inline uint64_t read_tsc(void)
+{
+    uint32_t low, high;
+    asm volatile("rdtsc" : "=a" (low), "=d" (high));
+    return ((uint64_t)low) | (((uint64_t)high) << 32);
+}
+
+template <typename T>
+inline int compare(T a, T b) {
+    if (a == b)
+        return 0;
+    else
+        return a < b ? -1 : 1;
+}
+
+
+/** Type traits **/
+namespace mass {
+
+template <typename T> struct type_synonym {
+    typedef T type;
+};
+
+
+#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS
+template <typename T, T V>
+using integral_constant = std::integral_constant<T, V>;
+typedef std::true_type true_type;
+typedef std::false_type false_type;
+#else
+template <typename T, T V>
+struct integral_constant {
+    typedef integral_constant<T, V> type;
+    typedef T value_type;
+    static constexpr T value = V;
+};
+template <typename T, T V> constexpr T integral_constant<T, V>::value;
+typedef integral_constant<bool, true> true_type;
+typedef integral_constant<bool, false> false_type;
+#endif
+
+#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS
+template <bool B, typename T, typename F>
+using conditional = std::conditional<B, T, F>;
+#else
+template <bool B, typename T, typename F> struct conditional {};
+template <typename T, typename F> struct conditional<true, T, F> {
+    typedef T type;
+};
+template <typename T, typename F> struct conditional<false, T, F> {
+    typedef F type;
+};
+#endif
+
+#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS
+template <typename T> using remove_const = std::remove_const<T>;
+template <typename T> using remove_volatile = std::remove_volatile<T>;
+template <typename T> using remove_cv = std::remove_cv<T>;
+#else
+template <typename T> struct remove_const : public type_synonym<T> {};
+template <typename T> struct remove_const<const T> : public type_synonym<T> {};
+template <typename T> struct remove_volatile : public type_synonym<T> {};
+template <typename T> struct remove_volatile<volatile T> : public type_synonym<T> {};
+template <typename T> struct remove_cv {
+    typedef typename remove_const<typename remove_volatile<T>::type>::type type;
+};
+#endif
+
+#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS
+template <typename T> using is_pointer = std::is_pointer<T>;
+#else
+template <typename T> struct is_pointer_helper : public false_type {};
+template <typename T> struct is_pointer_helper<T*> : public true_type {};
+template <typename T> struct is_pointer
+    : public integral_constant<bool, is_pointer_helper<typename remove_cv<T>::type>::value> {};
+#endif
+
+#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS
+template <typename T> using is_reference = std::is_reference<T>;
+#else
+template <typename T> struct is_reference_helper : public false_type {};
+template <typename T> struct is_reference_helper<T&> : public true_type {};
+#if HAVE_CXX_RVALUE_REFERENCES
+template <typename T> struct is_reference_helper<T&&> : public true_type {};
+#endif
+template <typename T> struct is_reference
+    : public integral_constant<bool, is_reference_helper<typename remove_cv<T>::type>::value> {};
+#endif
+
+#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS
+template <typename T> using make_unsigned = std::make_unsigned<T>;
+template <typename T> using make_signed = std::make_signed<T>;
+#else
+template <typename T> struct make_unsigned {};
+template <> struct make_unsigned<char> : public type_synonym<unsigned char> {};
+template <> struct make_unsigned<signed char> : public type_synonym<unsigned char> {};
+template <> struct make_unsigned<unsigned char> : public type_synonym<unsigned char> {};
+template <> struct make_unsigned<short> : public type_synonym<unsigned short> {};
+template <> struct make_unsigned<unsigned short> : public type_synonym<unsigned short> {};
+template <> struct make_unsigned<int> : public type_synonym<unsigned> {};
+template <> struct make_unsigned<unsigned> : public type_synonym<unsigned> {};
+template <> struct make_unsigned<long> : public type_synonym<unsigned long> {};
+template <> struct make_unsigned<unsigned long> : public type_synonym<unsigned long> {};
+template <> struct make_unsigned<long long> : public type_synonym<unsigned long long> {};
+template <> struct make_unsigned<unsigned long long> : public type_synonym<unsigned long long> {};
+
+template <typename T> struct make_signed {};
+template <> struct make_signed<char> : public type_synonym<signed char> {};
+template <> struct make_signed<signed char> : public type_synonym<signed char> {};
+template <> struct make_signed<unsigned char> : public type_synonym<signed char> {};
+template <> struct make_signed<short> : public type_synonym<short> {};
+template <> struct make_signed<unsigned short> : public type_synonym<short> {};
+template <> struct make_signed<int> : public type_synonym<int> {};
+template <> struct make_signed<unsigned> : public type_synonym<int> {};
+template <> struct make_signed<long> : public type_synonym<long> {};
+template <> struct make_signed<unsigned long> : public type_synonym<long> {};
+template <> struct make_signed<long long> : public type_synonym<long long> {};
+template <> struct make_signed<unsigned long long> : public type_synonym<long long> {};
+#endif
+
+/** @class is_trivially_copyable
+  @brief Template determining whether T may be copied by memcpy.
+
+  is_trivially_copyable<T> is equivalent to true_type if T has a trivial
+  copy constructor, false_type if it does not. */
+
+#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS && HAVE_STD_IS_TRIVIALLY_COPYABLE
+template <typename T> using is_trivially_copyable = std::is_trivially_copyable<T>;
+#elif HAVE___HAS_TRIVIAL_COPY
+template <typename T> struct is_trivially_copyable : public integral_constant<bool, __has_trivial_copy(T)> {};
+#else
+template <typename T> struct is_trivially_copyable : public false_type {};
+template <> struct is_trivially_copyable<unsigned char> : public true_type {};
+template <> struct is_trivially_copyable<signed char> : public true_type {};
+template <> struct is_trivially_copyable<char> : public true_type {};
+template <> struct is_trivially_copyable<unsigned short> : public true_type {};
+template <> struct is_trivially_copyable<short> : public true_type {};
+template <> struct is_trivially_copyable<unsigned> : public true_type {};
+template <> struct is_trivially_copyable<int> : public true_type {};
+template <> struct is_trivially_copyable<unsigned long> : public true_type {};
+template <> struct is_trivially_copyable<long> : public true_type {};
+template <> struct is_trivially_copyable<unsigned long long> : public true_type {};
+template <> struct is_trivially_copyable<long long> : public true_type {};
+template <typename T> struct is_trivially_copyable<T *> : public true_type {};
+#endif
+
+
+/** @class fast_argument
+  @brief Template defining a fast argument type for objects of type T.
+
+  fast_argument<T>::type equals either "const T &" or "T".
+  fast_argument<T>::is_reference is true iff fast_argument<T>::type is
+  a reference. If fast_argument<T>::is_reference is true, then
+  fast_argument<T>::enable_rvalue_reference is a typedef to void; otherwise
+  it is not defined. */
+template <typename T, bool use_reference = (!is_reference<T>::value
+                                            && (!is_trivially_copyable<T>::value
+                                                || sizeof(T) > sizeof(void *)))>
+struct fast_argument;
+
+template <typename T> struct fast_argument<T, true> {
+    static constexpr bool is_reference = true;
+    typedef const T &type;
+#if HAVE_CXX_RVALUE_REFERENCES
+    typedef void enable_rvalue_reference;
+#endif
+};
+template <typename T> struct fast_argument<T, false> {
+    static constexpr bool is_reference = false;
+    typedef T type;
+};
+template <typename T> constexpr bool fast_argument<T, true>::is_reference;
+template <typename T> constexpr bool fast_argument<T, false>::is_reference;
+
+}
+
+
+template <typename T>
+struct has_fast_int_multiply : public mass::false_type {
+    // enum { check_t_integral = mass::integer_traits<T>::is_signed };
+};
+
+#if defined(__i386__) || defined(__x86_64__)
+inline void int_multiply(unsigned a, unsigned b, unsigned &xlow, unsigned &xhigh)
+{
+    __asm__("mul %2" : "=a" (xlow), "=d" (xhigh) : "r" (a), "a" (b) : "cc");
+}
+template <> struct has_fast_int_multiply<unsigned> : public mass::true_type {};
+
+# if SIZEOF_LONG == 4 || (defined(__x86_64__) && SIZEOF_LONG == 8)
+inline void int_multiply(unsigned long a, unsigned long b, unsigned long &xlow, unsigned long &xhigh)
+{
+    __asm__("mul %2" : "=a" (xlow), "=d" (xhigh) : "r" (a), "a" (b) : "cc");
+}
+template <> struct has_fast_int_multiply<unsigned long> : public mass::true_type {};
+# endif
+
+# if defined(__x86_64__) && SIZEOF_LONG_LONG == 8
+inline void int_multiply(unsigned long long a, unsigned long long b, unsigned long long &xlow, unsigned long long &xhigh)
+{
+    __asm__("mul %2" : "=a" (xlow), "=d" (xhigh) : "r" (a), "a" (b) : "cc");
+}
+template <> struct has_fast_int_multiply<unsigned long long> : public mass::true_type {};
+# endif
+#endif
+
+struct uninitialized_type {};
+
+#endif
diff --git a/silo/masstree/config.h b/silo/masstree/config.h
new file mode 100644 (file)
index 0000000..8acdb66
--- /dev/null
@@ -0,0 +1,326 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+#ifndef MASSTREE_CONFIG_H_INCLUDED
+#define MASSTREE_CONFIG_H_INCLUDED 1
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Assumed size of a cache line. */
+#define CACHE_LINE_SIZE 64
+
+/* Define to enable debugging assertions. */
+/* #undef ENABLE_ASSERTIONS */
+
+/* Define to enable invariant assertions. */
+#define ENABLE_INVARIANTS 1
+
+/* Define to enable precondition assertions. */
+#define ENABLE_PRECONDITIONS 1
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+
+/* Define if the C++ compiler understands 'auto'. */
+#define HAVE_CXX_AUTO 1
+
+/* Define if the C++ compiler understands constexpr. */
+#define HAVE_CXX_CONSTEXPR 1
+
+/* Define if the C++ compiler understands rvalue references. */
+#define HAVE_CXX_RVALUE_REFERENCES 1
+
+/* Define if the C++ compiler understands static_assert. */
+#define HAVE_CXX_STATIC_ASSERT 1
+
+/* Define if the C++ compiler understands template alias. */
+#define HAVE_CXX_TEMPLATE_ALIAS 1
+
+/* Define to 1 if you have the declaration of `clock_gettime', and to 0 if you
+   don't. */
+#define HAVE_DECL_CLOCK_GETTIME 1
+
+/* Define to 1 if you have the declaration of `getline', and to 0 if you
+   don't. */
+#define HAVE_DECL_GETLINE 1
+
+/* Define to 1 if you have the <execinfo.h> header file. */
+#define HAVE_EXECINFO_H 1
+
+/* Define if you are using libflow for malloc. */
+/* #undef HAVE_FLOW_MALLOC */
+
+/* Define if you are using libhoard for malloc. */
+/* #undef HAVE_HOARD_MALLOC */
+
+/* Define if int64_t and long are the same type. */
+#define HAVE_INT64_T_IS_LONG 1
+
+/* Define if int64_t and long long are the same type. */
+/* #undef HAVE_INT64_T_IS_LONG_LONG */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if you are using libjemalloc for malloc. */
+#define HAVE_JEMALLOC 1
+
+/* Define if you have libnuma. */
+#define HAVE_LIBNUMA 1
+
+/* Define to 1 if the system has the type `long long'. */
+#define HAVE_LONG_LONG 1
+
+/* Define if MADV_HUGEPAGE is supported. */
+#define HAVE_MADV_HUGEPAGE 1
+
+/* Define if MAP_HUGETLB is supported. */
+#define HAVE_MAP_HUGETLB 1
+
+/* Define if memory debugging support is enabled. */
+/* #undef HAVE_MEMDEBUG */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <numa.h> header file. */
+#define HAVE_NUMA_H 1
+
+/* Define if off_t and long are the same type. */
+#define HAVE_OFF_T_IS_LONG 1
+
+/* Define if off_t and long long are the same type. */
+/* #undef HAVE_OFF_T_IS_LONG_LONG */
+
+/* Define if size_t and unsigned are the same type. */
+/* #undef HAVE_SIZE_T_IS_UNSIGNED */
+
+/* Define if size_t and unsigned long are the same type. */
+#define HAVE_SIZE_T_IS_UNSIGNED_LONG 1
+
+/* Define if size_t and unsigned long long are the same type. */
+/* #undef HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have std::hash. */
+#define HAVE_STD_HASH 1
+
+/* Define if you have the std::is_rvalue_reference template. */
+#define HAVE_STD_IS_RVALUE_REFERENCE 1
+
+/* Define if you have the std::is_trivially_copyable template. */
+#define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if superpage support is enabled. */
+#define HAVE_SUPERPAGE 1
+
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+#define HAVE_SYS_EPOLL_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define if you are using libtcmalloc for malloc. */
+/* #undef HAVE_TCMALLOC */
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <type_traits> header file. */
+#define HAVE_TYPE_TRAITS 1
+
+/* Define if unaligned accesses are OK. */
+#define HAVE_UNALIGNED_ACCESS 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the __builtin_clz builtin. */
+#define HAVE___BUILTIN_CLZ 1
+
+/* Define if you have the __builtin_clzl builtin. */
+#define HAVE___BUILTIN_CLZL 1
+
+/* Define if you have the __builtin_clzll builtin. */
+#define HAVE___BUILTIN_CLZLL 1
+
+/* Define if you have the __builtin_ctz builtin. */
+#define HAVE___BUILTIN_CTZ 1
+
+/* Define if you have the __builtin_ctzl builtin. */
+#define HAVE___BUILTIN_CTZL 1
+
+/* Define if you have the __builtin_ctzll builtin. */
+#define HAVE___BUILTIN_CTZLL 1
+
+/* Define if you have the __has_trivial_copy compiler intrinsic. */
+#define HAVE___HAS_TRIVIAL_COPY 1
+
+/* Define if you have the __sync_add_and_fetch builtin. */
+#define HAVE___SYNC_ADD_AND_FETCH 1
+
+/* Define if you have the __sync_add_and_fetch_8 builtin. */
+#define HAVE___SYNC_ADD_AND_FETCH_8 1
+
+/* Define if you have the __sync_bool_compare_and_swap builtin. */
+#define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+
+/* Define if you have the __sync_bool_compare_and_swap_8 builtin. */
+#define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+
+/* Define if you have the __sync_fetch_and_add builtin. */
+#define HAVE___SYNC_FETCH_AND_ADD 1
+
+/* Define if you have the __sync_fetch_and_add_8 builtin. */
+#define HAVE___SYNC_FETCH_AND_ADD_8 1
+
+/* Define if you have the __sync_fetch_and_or builtin. */
+#define HAVE___SYNC_FETCH_AND_OR 1
+
+/* Define if you have the __sync_fetch_and_or_8 builtin. */
+#define HAVE___SYNC_FETCH_AND_OR_8 1
+
+/* Define if you have the __sync_lock_release_set builtin. */
+#define HAVE___SYNC_LOCK_RELEASE_SET 1
+
+/* Define if you have the __sync_lock_test_and_set builtin. */
+#define HAVE___SYNC_LOCK_TEST_AND_SET 1
+
+/* Define if you have the __sync_lock_test_and_set_val builtin. */
+#define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+
+/* Define if you have the __sync_or_and_fetch builtin. */
+#define HAVE___SYNC_OR_AND_FETCH 1
+
+/* Define if you have the __sync_or_and_fetch_8 builtin. */
+#define HAVE___SYNC_OR_AND_FETCH_8 1
+
+/* Define if you have the __sync_synchronize builtin. */
+#define HAVE___SYNC_SYNCHRONIZE 1
+
+/* Define if you have the __sync_val_compare_and_swap builtin. */
+#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+
+/* Define if you have the __sync_val_compare_and_swap_8 builtin. */
+#define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+
+/* Maximum key length */
+#define MASSTREE_MAXKEYLEN 1024
+
+/* Define if the default row type is value_timed_array. */
+/* #undef MASSTREE_ROW_TYPE_ARRAY */
+
+/* Define if the default row type is value_timed_array_ver. */
+/* #undef MASSTREE_ROW_TYPE_ARRAY_VER */
+
+/* Define if the default row type is value_timed_bag. */
+#define MASSTREE_ROW_TYPE_BAG 1
+
+/* Define if the default row type is value_timed_str. */
+/* #undef MASSTREE_ROW_TYPE_STR */
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "masstree-beta"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "masstree-beta 0.1"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "masstree-beta"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.1"
+
+/* The size of `int', as computed by sizeof. */
+#define SIZEOF_INT 4
+
+/* The size of `long', as computed by sizeof. */
+#define SIZEOF_LONG 8
+
+/* The size of `long long', as computed by sizeof. */
+#define SIZEOF_LONG_LONG 8
+
+/* The size of `short', as computed by sizeof. */
+#define SIZEOF_SHORT 2
+
+/* The size of `void *', as computed by sizeof. */
+#define SIZEOF_VOID_P 8
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* #  undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Define if WORDS_BIGENDIAN has been set. */
+#define WORDS_BIGENDIAN_SET 1
+
+#if !FORCE_ENABLE_ASSERTIONS && !ENABLE_ASSERTIONS
+# define NDEBUG 1
+#endif
+
+/** @brief Assert macro that always runs. */
+extern void fail_always_assert(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#define always_assert(x, ...) do { if (!(x)) fail_always_assert(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#define mandatory_assert always_assert
+
+/** @brief Assert macro for invariants.
+
+    masstree_invariant(x) is executed if --enable-invariants or
+    --enable-assertions. */
+extern void fail_masstree_invariant(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_INVARIANTS) && ENABLE_ASSERTIONS) || ENABLE_INVARIANTS
+#define masstree_invariant(x, ...) do { if (!(x)) fail_masstree_invariant(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#else
+#define masstree_invariant(x, ...) do { } while (0)
+#endif
+
+/** @brief Assert macro for preconditions.
+
+    masstree_precondition(x) is executed if --enable-preconditions or
+    --enable-assertions. */
+extern void fail_masstree_precondition(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_PRECONDITIONS) && ENABLE_ASSERTIONS) || ENABLE_PRECONDITIONS
+#define masstree_precondition(x, ...) do { if (!(x)) fail_masstree_precondition(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#else
+#define masstree_precondition(x, ...) do { } while (0)
+#endif
+
+#ifndef invariant
+#define invariant masstree_invariant
+#endif
+#ifndef precondition
+#define precondition masstree_precondition
+#endif
+
+#endif
diff --git a/silo/masstree/config.h.in b/silo/masstree/config.h.in
new file mode 100644 (file)
index 0000000..ea72bd4
--- /dev/null
@@ -0,0 +1,325 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+#ifndef MASSTREE_CONFIG_H_INCLUDED
+#define MASSTREE_CONFIG_H_INCLUDED 1
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Assumed size of a cache line. */
+#undef CACHE_LINE_SIZE
+
+/* Define to enable debugging assertions. */
+#undef ENABLE_ASSERTIONS
+
+/* Define to enable invariant assertions. */
+#undef ENABLE_INVARIANTS
+
+/* Define to enable precondition assertions. */
+#undef ENABLE_PRECONDITIONS
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#undef HAVE_CLOCK_GETTIME
+
+/* Define if the C++ compiler understands 'auto'. */
+#undef HAVE_CXX_AUTO
+
+/* Define if the C++ compiler understands constexpr. */
+#undef HAVE_CXX_CONSTEXPR
+
+/* Define if the C++ compiler understands rvalue references. */
+#undef HAVE_CXX_RVALUE_REFERENCES
+
+/* Define if the C++ compiler understands static_assert. */
+#undef HAVE_CXX_STATIC_ASSERT
+
+/* Define if the C++ compiler understands template alias. */
+#undef HAVE_CXX_TEMPLATE_ALIAS
+
+/* Define to 1 if you have the declaration of `clock_gettime', and to 0 if you
+   don't. */
+#undef HAVE_DECL_CLOCK_GETTIME
+
+/* Define to 1 if you have the declaration of `getline', and to 0 if you
+   don't. */
+#undef HAVE_DECL_GETLINE
+
+/* Define to 1 if you have the <execinfo.h> header file. */
+#undef HAVE_EXECINFO_H
+
+/* Define if you are using libflow for malloc. */
+#undef HAVE_FLOW_MALLOC
+
+/* Define if you are using libhoard for malloc. */
+#undef HAVE_HOARD_MALLOC
+
+/* Define if int64_t and long are the same type. */
+#undef HAVE_INT64_T_IS_LONG
+
+/* Define if int64_t and long long are the same type. */
+#undef HAVE_INT64_T_IS_LONG_LONG
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define if you are using libjemalloc for malloc. */
+#undef HAVE_JEMALLOC
+
+/* Define if you have libnuma. */
+#undef HAVE_LIBNUMA
+
+/* Define to 1 if the system has the type `long long'. */
+#undef HAVE_LONG_LONG
+
+/* Define if MADV_HUGEPAGE is supported. */
+#undef HAVE_MADV_HUGEPAGE
+
+/* Define if MAP_HUGETLB is supported. */
+#undef HAVE_MAP_HUGETLB
+
+/* Define if memory debugging support is enabled. */
+#undef HAVE_MEMDEBUG
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <numa.h> header file. */
+#undef HAVE_NUMA_H
+
+/* Define if off_t and long are the same type. */
+#undef HAVE_OFF_T_IS_LONG
+
+/* Define if off_t and long long are the same type. */
+#undef HAVE_OFF_T_IS_LONG_LONG
+
+/* Define if size_t and unsigned are the same type. */
+#undef HAVE_SIZE_T_IS_UNSIGNED
+
+/* Define if size_t and unsigned long are the same type. */
+#undef HAVE_SIZE_T_IS_UNSIGNED_LONG
+
+/* Define if size_t and unsigned long long are the same type. */
+#undef HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define if you have std::hash. */
+#undef HAVE_STD_HASH
+
+/* Define if you have the std::is_rvalue_reference template. */
+#undef HAVE_STD_IS_RVALUE_REFERENCE
+
+/* Define if you have the std::is_trivially_copyable template. */
+#undef HAVE_STD_IS_TRIVIALLY_COPYABLE
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define if superpage support is enabled. */
+#undef HAVE_SUPERPAGE
+
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+#undef HAVE_SYS_EPOLL_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define if you are using libtcmalloc for malloc. */
+#undef HAVE_TCMALLOC
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define to 1 if you have the <type_traits> header file. */
+#undef HAVE_TYPE_TRAITS
+
+/* Define if unaligned accesses are OK. */
+#undef HAVE_UNALIGNED_ACCESS
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the __builtin_clz builtin. */
+#undef HAVE___BUILTIN_CLZ
+
+/* Define if you have the __builtin_clzl builtin. */
+#undef HAVE___BUILTIN_CLZL
+
+/* Define if you have the __builtin_clzll builtin. */
+#undef HAVE___BUILTIN_CLZLL
+
+/* Define if you have the __builtin_ctz builtin. */
+#undef HAVE___BUILTIN_CTZ
+
+/* Define if you have the __builtin_ctzl builtin. */
+#undef HAVE___BUILTIN_CTZL
+
+/* Define if you have the __builtin_ctzll builtin. */
+#undef HAVE___BUILTIN_CTZLL
+
+/* Define if you have the __has_trivial_copy compiler intrinsic. */
+#undef HAVE___HAS_TRIVIAL_COPY
+
+/* Define if you have the __sync_add_and_fetch builtin. */
+#undef HAVE___SYNC_ADD_AND_FETCH
+
+/* Define if you have the __sync_add_and_fetch_8 builtin. */
+#undef HAVE___SYNC_ADD_AND_FETCH_8
+
+/* Define if you have the __sync_bool_compare_and_swap builtin. */
+#undef HAVE___SYNC_BOOL_COMPARE_AND_SWAP
+
+/* Define if you have the __sync_bool_compare_and_swap_8 builtin. */
+#undef HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8
+
+/* Define if you have the __sync_fetch_and_add builtin. */
+#undef HAVE___SYNC_FETCH_AND_ADD
+
+/* Define if you have the __sync_fetch_and_add_8 builtin. */
+#undef HAVE___SYNC_FETCH_AND_ADD_8
+
+/* Define if you have the __sync_fetch_and_or builtin. */
+#undef HAVE___SYNC_FETCH_AND_OR
+
+/* Define if you have the __sync_fetch_and_or_8 builtin. */
+#undef HAVE___SYNC_FETCH_AND_OR_8
+
+/* Define if you have the __sync_lock_release_set builtin. */
+#undef HAVE___SYNC_LOCK_RELEASE_SET
+
+/* Define if you have the __sync_lock_test_and_set builtin. */
+#undef HAVE___SYNC_LOCK_TEST_AND_SET
+
+/* Define if you have the __sync_lock_test_and_set_val builtin. */
+#undef HAVE___SYNC_LOCK_TEST_AND_SET_VAL
+
+/* Define if you have the __sync_or_and_fetch builtin. */
+#undef HAVE___SYNC_OR_AND_FETCH
+
+/* Define if you have the __sync_or_and_fetch_8 builtin. */
+#undef HAVE___SYNC_OR_AND_FETCH_8
+
+/* Define if you have the __sync_synchronize builtin. */
+#undef HAVE___SYNC_SYNCHRONIZE
+
+/* Define if you have the __sync_val_compare_and_swap builtin. */
+#undef HAVE___SYNC_VAL_COMPARE_AND_SWAP
+
+/* Define if you have the __sync_val_compare_and_swap_8 builtin. */
+#undef HAVE___SYNC_VAL_COMPARE_AND_SWAP_8
+
+/* Maximum key length */
+#undef MASSTREE_MAXKEYLEN
+
+/* Define if the default row type is value_timed_array. */
+#undef MASSTREE_ROW_TYPE_ARRAY
+
+/* Define if the default row type is value_timed_array_ver. */
+#undef MASSTREE_ROW_TYPE_ARRAY_VER
+
+/* Define if the default row type is value_timed_bag. */
+#undef MASSTREE_ROW_TYPE_BAG
+
+/* Define if the default row type is value_timed_str. */
+#undef MASSTREE_ROW_TYPE_STR
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* The size of `int', as computed by sizeof. */
+#undef SIZEOF_INT
+
+/* The size of `long', as computed by sizeof. */
+#undef SIZEOF_LONG
+
+/* The size of `long long', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG
+
+/* The size of `short', as computed by sizeof. */
+#undef SIZEOF_SHORT
+
+/* The size of `void *', as computed by sizeof. */
+#undef SIZEOF_VOID_P
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+#  undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define if WORDS_BIGENDIAN has been set. */
+#undef WORDS_BIGENDIAN_SET
+
+#if !FORCE_ENABLE_ASSERTIONS && !ENABLE_ASSERTIONS
+# define NDEBUG 1
+#endif
+
+/** @brief Assert macro that always runs. */
+extern void fail_always_assert(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#define always_assert(x, ...) do { if (!(x)) fail_always_assert(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#define mandatory_assert always_assert
+
+/** @brief Assert macro for invariants.
+
+    masstree_invariant(x) is executed if --enable-invariants or
+    --enable-assertions. */
+extern void fail_masstree_invariant(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_INVARIANTS) && ENABLE_ASSERTIONS) || ENABLE_INVARIANTS
+#define masstree_invariant(x, ...) do { if (!(x)) fail_masstree_invariant(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#else
+#define masstree_invariant(x, ...) do { } while (0)
+#endif
+
+/** @brief Assert macro for preconditions.
+
+    masstree_precondition(x) is executed if --enable-preconditions or
+    --enable-assertions. */
+extern void fail_masstree_precondition(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_PRECONDITIONS) && ENABLE_ASSERTIONS) || ENABLE_PRECONDITIONS
+#define masstree_precondition(x, ...) do { if (!(x)) fail_masstree_precondition(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#else
+#define masstree_precondition(x, ...) do { } while (0)
+#endif
+
+#ifndef invariant
+#define invariant masstree_invariant
+#endif
+#ifndef precondition
+#define precondition masstree_precondition
+#endif
+
+#endif
diff --git a/silo/masstree/config.log b/silo/masstree/config.log
new file mode 100644 (file)
index 0000000..db63de7
--- /dev/null
@@ -0,0 +1,1511 @@
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by masstree-beta configure 0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ ./configure --enable-max-key-len=1024 --disable-assertions --enable-invariants --enable-preconditions --with-malloc=jemalloc
+
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = dw-10
+uname -m = x86_64
+uname -r = 4.4.0-119-generic
+uname -s = Linux
+uname -v = #143-Ubuntu SMP Mon Apr 2 16:08:24 UTC 2018
+
+/usr/bin/uname -p = unknown
+/bin/uname -X     = unknown
+
+/bin/arch              = unknown
+/usr/bin/arch -k       = unknown
+/usr/convex/getsysinfo = unknown
+/usr/bin/hostinfo      = unknown
+/bin/machine           = unknown
+/usr/bin/oslevel       = unknown
+/bin/universe          = unknown
+
+PATH: /scratch/build/bin
+PATH: /home/ahmad/bin
+PATH: /usr/local/sbin
+PATH: /usr/local/bin
+PATH: /usr/sbin
+PATH: /usr/bin
+PATH: /sbin
+PATH: /bin
+PATH: /usr/games
+PATH: /usr/local/games
+PATH: /snap/bin
+PATH: /usr/lib/jvm/java-8-oracle/bin
+PATH: /usr/lib/jvm/java-8-oracle/db/bin
+PATH: /usr/lib/jvm/java-8-oracle/jre/bin
+PATH: /usr/lib/go/bin
+PATH: /home/ahmad/go/bin
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+configure:2507: checking for gcc
+configure:2523: found /usr/bin/gcc
+configure:2534: result: gcc
+configure:2763: checking for C compiler version
+configure:2772: gcc --version >&5
+gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
+Copyright (C) 2015 Free Software Foundation, Inc.
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+configure:2783: $? = 0
+configure:2772: gcc -v >&5
+Using built-in specs.
+COLLECT_GCC=gcc
+COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.11' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.11) 
+configure:2783: $? = 0
+configure:2772: gcc -V >&5
+gcc: error: unrecognized command line option '-V'
+gcc: fatal error: no input files
+compilation terminated.
+configure:2783: $? = 1
+configure:2772: gcc -qversion >&5
+gcc: error: unrecognized command line option '-qversion'
+gcc: fatal error: no input files
+compilation terminated.
+configure:2783: $? = 1
+configure:2803: checking whether the C compiler works
+configure:2825: gcc    conftest.c  >&5
+configure:2829: $? = 0
+configure:2877: result: yes
+configure:2880: checking for C compiler default output file name
+configure:2882: result: a.out
+configure:2888: checking for suffix of executables
+configure:2895: gcc -o conftest    conftest.c  >&5
+configure:2899: $? = 0
+configure:2921: result: 
+configure:2943: checking whether we are cross compiling
+configure:2951: gcc -o conftest    conftest.c  >&5
+configure:2955: $? = 0
+configure:2962: ./conftest
+configure:2966: $? = 0
+configure:2981: result: no
+configure:2986: checking for suffix of object files
+configure:3008: gcc -c   conftest.c >&5
+configure:3012: $? = 0
+configure:3033: result: o
+configure:3037: checking whether we are using the GNU C compiler
+configure:3056: gcc -c   conftest.c >&5
+configure:3056: $? = 0
+configure:3065: result: yes
+configure:3074: checking whether gcc accepts -g
+configure:3094: gcc -c -g  conftest.c >&5
+configure:3094: $? = 0
+configure:3135: result: yes
+configure:3152: checking for gcc option to accept ISO C89
+configure:3215: gcc  -c -g -O2  conftest.c >&5
+configure:3215: $? = 0
+configure:3228: result: none needed
+configure:3306: checking for g++
+configure:3322: found /usr/bin/g++
+configure:3333: result: g++
+configure:3360: checking for C++ compiler version
+configure:3369: g++ --version >&5
+g++ (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
+Copyright (C) 2015 Free Software Foundation, Inc.
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+configure:3380: $? = 0
+configure:3369: g++ -v >&5
+Using built-in specs.
+COLLECT_GCC=g++
+COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.11' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.11) 
+configure:3380: $? = 0
+configure:3369: g++ -V >&5
+g++: error: unrecognized command line option '-V'
+g++: fatal error: no input files
+compilation terminated.
+configure:3380: $? = 1
+configure:3369: g++ -qversion >&5
+g++: error: unrecognized command line option '-qversion'
+g++: fatal error: no input files
+compilation terminated.
+configure:3380: $? = 1
+configure:3384: checking whether we are using the GNU C++ compiler
+configure:3403: g++ -c   conftest.cpp >&5
+configure:3403: $? = 0
+configure:3412: result: yes
+configure:3421: checking whether g++ accepts -g
+configure:3441: g++ -c -g  conftest.cpp >&5
+configure:3441: $? = 0
+configure:3482: result: yes
+configure:3521: checking how to run the C++ preprocessor
+configure:3548: g++ -E  conftest.cpp
+configure:3548: $? = 0
+configure:3562: g++ -E  conftest.cpp
+conftest.cpp:10:28: fatal error: ac_nonexistent.h: No such file or directory
+compilation terminated.
+configure:3562: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| /* end confdefs.h.  */
+| #include <ac_nonexistent.h>
+configure:3587: result: g++ -E
+configure:3607: g++ -E  conftest.cpp
+configure:3607: $? = 0
+configure:3621: g++ -E  conftest.cpp
+conftest.cpp:10:28: fatal error: ac_nonexistent.h: No such file or directory
+compilation terminated.
+configure:3621: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| /* end confdefs.h.  */
+| #include <ac_nonexistent.h>
+configure:3650: checking for grep that handles long lines and -e
+configure:3708: result: /bin/grep
+configure:3713: checking for egrep
+configure:3775: result: /bin/grep -E
+configure:3780: checking for ANSI C header files
+configure:3800: g++ -c -g -O2  conftest.cpp >&5
+configure:3800: $? = 0
+configure:3873: g++ -o conftest -g -O2   conftest.cpp  >&5
+configure:3873: $? = 0
+configure:3873: ./conftest
+configure:3873: $? = 0
+configure:3884: result: yes
+configure:3897: checking for sys/types.h
+configure:3897: g++ -c -g -O2  conftest.cpp >&5
+configure:3897: $? = 0
+configure:3897: result: yes
+configure:3897: checking for sys/stat.h
+configure:3897: g++ -c -g -O2  conftest.cpp >&5
+configure:3897: $? = 0
+configure:3897: result: yes
+configure:3897: checking for stdlib.h
+configure:3897: g++ -c -g -O2  conftest.cpp >&5
+configure:3897: $? = 0
+configure:3897: result: yes
+configure:3897: checking for string.h
+configure:3897: g++ -c -g -O2  conftest.cpp >&5
+configure:3897: $? = 0
+configure:3897: result: yes
+configure:3897: checking for memory.h
+configure:3897: g++ -c -g -O2  conftest.cpp >&5
+configure:3897: $? = 0
+configure:3897: result: yes
+configure:3897: checking for strings.h
+configure:3897: g++ -c -g -O2  conftest.cpp >&5
+configure:3897: $? = 0
+configure:3897: result: yes
+configure:3897: checking for inttypes.h
+configure:3897: g++ -c -g -O2  conftest.cpp >&5
+configure:3897: $? = 0
+configure:3897: result: yes
+configure:3897: checking for stdint.h
+configure:3897: g++ -c -g -O2  conftest.cpp >&5
+configure:3897: $? = 0
+configure:3897: result: yes
+configure:3897: checking for unistd.h
+configure:3897: g++ -c -g -O2  conftest.cpp >&5
+configure:3897: $? = 0
+configure:3897: result: yes
+configure:3909: checking whether byte ordering is bigendian
+configure:3924: g++ -c -g -O2  conftest.cpp >&5
+conftest.cpp:21:9: error: expected unqualified-id before 'not' token
+         not a universal capable compiler
+         ^
+configure:3924: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| /* end confdefs.h.  */
+| #ifndef __APPLE_CC__
+|             not a universal capable compiler
+|           #endif
+|           typedef int dummy;
+| 
+configure:3969: g++ -c -g -O2  conftest.cpp >&5
+configure:3969: $? = 0
+configure:3987: g++ -c -g -O2  conftest.cpp >&5
+conftest.cpp: In function 'int main()':
+conftest.cpp:27:8: error: 'big' was not declared in this scope
+    not big endian
+        ^
+configure:3987: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| /* end confdefs.h.  */
+| #include <sys/types.h>
+|              #include <sys/param.h>
+| 
+| int
+| main ()
+| {
+| #if BYTE_ORDER != BIG_ENDIAN
+|               not big endian
+|              #endif
+| 
+|   ;
+|   return 0;
+| }
+configure:4115: result: no
+configure:4137: checking sys/epoll.h usability
+configure:4137: g++ -c -g -O2  conftest.cpp >&5
+configure:4137: $? = 0
+configure:4137: result: yes
+configure:4137: checking sys/epoll.h presence
+configure:4137: g++ -E  conftest.cpp
+configure:4137: $? = 0
+configure:4137: result: yes
+configure:4137: checking for sys/epoll.h
+configure:4137: result: yes
+configure:4137: checking numa.h usability
+configure:4137: g++ -c -g -O2  conftest.cpp >&5
+configure:4137: $? = 0
+configure:4137: result: yes
+configure:4137: checking numa.h presence
+configure:4137: g++ -E  conftest.cpp
+configure:4137: $? = 0
+configure:4137: result: yes
+configure:4137: checking for numa.h
+configure:4137: result: yes
+configure:4148: checking for library containing numa_available
+configure:4179: g++ -o conftest -g -O2   conftest.cpp  >&5
+/tmp/cckSsuNW.o: In function `main':
+/home/ahmad/benchmark-fuzzying-tool/silo/masstree/conftest.cpp:33: undefined reference to `numa_available'
+collect2: error: ld returned 1 exit status
+configure:4179: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| #define HAVE_SYS_EPOLL_H 1
+| #define HAVE_NUMA_H 1
+| /* end confdefs.h.  */
+| 
+| /* Override any GCC internal prototype to avoid an error.
+|    Use char because int might match the return type of a GCC
+|    builtin and then its argument prototype would still apply.  */
+| #ifdef __cplusplus
+| extern "C"
+| #endif
+| char numa_available ();
+| int
+| main ()
+| {
+| return numa_available ();
+|   ;
+|   return 0;
+| }
+configure:4179: g++ -o conftest -g -O2   conftest.cpp -lnuma   >&5
+configure:4179: $? = 0
+configure:4196: result: -lnuma
+configure:4212: checking for __builtin_clz builtin
+configure:4228: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4228: $? = 0
+configure:4236: result: yes
+configure:4246: checking for __builtin_clzl builtin
+configure:4262: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4262: $? = 0
+configure:4270: result: yes
+configure:4280: checking for __builtin_clzll builtin
+configure:4296: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4296: $? = 0
+configure:4304: result: yes
+configure:4314: checking for __builtin_ctz builtin
+configure:4330: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4330: $? = 0
+configure:4338: result: yes
+configure:4348: checking for __builtin_ctzl builtin
+configure:4364: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4364: $? = 0
+configure:4372: result: yes
+configure:4382: checking for __builtin_ctzll builtin
+configure:4398: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4398: $? = 0
+configure:4406: result: yes
+configure:4416: checking for __sync_synchronize builtin
+configure:4433: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4433: $? = 0
+configure:4441: result: yes
+configure:4451: checking for __sync_fetch_and_add builtin
+configure:4467: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4467: $? = 0
+configure:4475: result: yes
+configure:4485: checking for __sync_add_and_fetch builtin
+configure:4501: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4501: $? = 0
+configure:4509: result: yes
+configure:4519: checking for __sync_fetch_and_add_8 builtin
+configure:4536: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4536: $? = 0
+configure:4544: result: yes
+configure:4554: checking for __sync_add_and_fetch_8 builtin
+configure:4571: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4571: $? = 0
+configure:4579: result: yes
+configure:4589: checking for __sync_fetch_and_or builtin
+configure:4605: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4605: $? = 0
+configure:4613: result: yes
+configure:4623: checking for __sync_or_and_fetch builtin
+configure:4639: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4639: $? = 0
+configure:4647: result: yes
+configure:4657: checking for __sync_fetch_and_or_8 builtin
+configure:4674: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4674: $? = 0
+configure:4682: result: yes
+configure:4692: checking for __sync_or_and_fetch_8 builtin
+configure:4709: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4709: $? = 0
+configure:4717: result: yes
+configure:4727: checking for __sync_bool_compare_and_swap builtin
+configure:4743: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4743: $? = 0
+configure:4751: result: yes
+configure:4761: checking for __sync_bool_compare_and_swap_8 builtin
+configure:4778: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4778: $? = 0
+configure:4786: result: yes
+configure:4796: checking for __sync_val_compare_and_swap builtin
+configure:4812: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4812: $? = 0
+configure:4820: result: yes
+configure:4830: checking for __sync_val_compare_and_swap_8 builtin
+configure:4847: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4847: $? = 0
+configure:4855: result: yes
+configure:4865: checking for __sync_lock_test_and_set builtin
+configure:4881: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4881: $? = 0
+configure:4889: result: yes
+configure:4899: checking for __sync_lock_test_and_set_val builtin
+configure:4915: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4915: $? = 0
+configure:4923: result: yes
+configure:4933: checking for __sync_lock_release_set builtin
+configure:4949: g++ -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:4949: $? = 0
+configure:4957: result: yes
+configure:4968: checking whether the C++ compiler understands 'auto'
+configure:4985: g++ -c -g -O2  conftest.cpp >&5
+conftest.cpp: In function 'int f(s)':
+conftest.cpp:45:41: error: ISO C++ forbids declaration of 'y' with no type [-fpermissive]
+ struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+                                         ^
+conftest.cpp:45:45: error: invalid initialization of reference of type 'int&' from expression of type 's'
+ struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+                                             ^
+conftest.cpp:45:57: error: request for member 'a' in 'y', which is of non-class type 'int'
+ struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+                                                         ^
+configure:4985: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| #define HAVE_SYS_EPOLL_H 1
+| #define HAVE_NUMA_H 1
+| #define HAVE_LIBNUMA 1
+| #define HAVE___BUILTIN_CLZ 1
+| #define HAVE___BUILTIN_CLZL 1
+| #define HAVE___BUILTIN_CLZLL 1
+| #define HAVE___BUILTIN_CTZ 1
+| #define HAVE___BUILTIN_CTZL 1
+| #define HAVE___BUILTIN_CTZLL 1
+| #define HAVE___SYNC_SYNCHRONIZE 1
+| #define HAVE___SYNC_FETCH_AND_ADD 1
+| #define HAVE___SYNC_ADD_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_ADD_8 1
+| #define HAVE___SYNC_ADD_AND_FETCH_8 1
+| #define HAVE___SYNC_FETCH_AND_OR 1
+| #define HAVE___SYNC_OR_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_OR_8 1
+| #define HAVE___SYNC_OR_AND_FETCH_8 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+| #define HAVE___SYNC_LOCK_RELEASE_SET 1
+| /* end confdefs.h.  */
+| struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+| int
+| main ()
+| {
+| 
+|   ;
+|   return 0;
+| }
+configure:4992: result: no
+configure:4996: checking whether the C++ compiler with -std=gnu++0x understands 'auto'
+configure:5009: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5009: $? = 0
+configure:5015: result: yes
+configure:5033: checking whether the C++ compiler understands constexpr
+configure:5050: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5050: $? = 0
+configure:5057: result: yes
+configure:5065: checking whether the C++ compiler understands static_assert
+configure:5082: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5082: $? = 0
+configure:5089: result: yes
+configure:5097: checking whether the C++ compiler understands rvalue references
+configure:5114: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5114: $? = 0
+configure:5121: result: yes
+configure:5129: checking whether the C++ compiler understands template alias
+configure:5146: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5146: $? = 0
+configure:5153: result: yes
+configure:5163: checking type_traits usability
+configure:5163: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5163: $? = 0
+configure:5163: result: yes
+configure:5163: checking type_traits presence
+configure:5163: g++ -E  conftest.cpp
+In file included from /usr/include/c++/5/type_traits:35:0,
+                 from conftest.cpp:50:
+/usr/include/c++/5/bits/c++0x_warning.h:32:2: error: #error This file requires compiler and library support for the ISO C++ 2011 standard. This support must be enabled with the -std=c++11 or -std=gnu++11 compiler options.
+ #error This file requires compiler and library support \
+  ^
+configure:5163: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| #define HAVE_SYS_EPOLL_H 1
+| #define HAVE_NUMA_H 1
+| #define HAVE_LIBNUMA 1
+| #define HAVE___BUILTIN_CLZ 1
+| #define HAVE___BUILTIN_CLZL 1
+| #define HAVE___BUILTIN_CLZLL 1
+| #define HAVE___BUILTIN_CTZ 1
+| #define HAVE___BUILTIN_CTZL 1
+| #define HAVE___BUILTIN_CTZLL 1
+| #define HAVE___SYNC_SYNCHRONIZE 1
+| #define HAVE___SYNC_FETCH_AND_ADD 1
+| #define HAVE___SYNC_ADD_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_ADD_8 1
+| #define HAVE___SYNC_ADD_AND_FETCH_8 1
+| #define HAVE___SYNC_FETCH_AND_OR 1
+| #define HAVE___SYNC_OR_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_OR_8 1
+| #define HAVE___SYNC_OR_AND_FETCH_8 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+| #define HAVE___SYNC_LOCK_RELEASE_SET 1
+| #define HAVE_CXX_AUTO 1
+| #define HAVE_CXX_CONSTEXPR 1
+| #define HAVE_CXX_STATIC_ASSERT 1
+| #define HAVE_CXX_RVALUE_REFERENCES 1
+| #define HAVE_CXX_TEMPLATE_ALIAS 1
+| /* end confdefs.h.  */
+| #include <type_traits>
+configure:5163: result: no
+configure:5163: WARNING: type_traits: accepted by the compiler, rejected by the preprocessor!
+configure:5163: WARNING: type_traits: proceeding with the compiler's result
+configure:5163: checking for type_traits
+configure:5163: result: yes
+configure:5174: checking for std::hash
+configure:5192: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5192: $? = 0
+configure:5199: result: yes
+configure:5207: checking for __has_trivial_copy
+configure:5224: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5224: $? = 0
+configure:5231: result: yes
+configure:5240: checking for std::move
+configure:5253: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5253: $? = 0
+configure:5259: result: yes
+configure:5271: checking for std::is_trivially_copyable
+configure:5288: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5288: $? = 0
+configure:5295: result: yes
+configure:5303: checking for std::is_rvalue_reference
+configure:5320: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5320: $? = 0
+configure:5327: result: yes
+configure:5337: checking for malloc in -lflow
+configure:5362: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -lflow  -lnuma  >&5
+/usr/bin/ld: cannot find -lflow
+collect2: error: ld returned 1 exit status
+configure:5362: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| #define HAVE_SYS_EPOLL_H 1
+| #define HAVE_NUMA_H 1
+| #define HAVE_LIBNUMA 1
+| #define HAVE___BUILTIN_CLZ 1
+| #define HAVE___BUILTIN_CLZL 1
+| #define HAVE___BUILTIN_CLZLL 1
+| #define HAVE___BUILTIN_CTZ 1
+| #define HAVE___BUILTIN_CTZL 1
+| #define HAVE___BUILTIN_CTZLL 1
+| #define HAVE___SYNC_SYNCHRONIZE 1
+| #define HAVE___SYNC_FETCH_AND_ADD 1
+| #define HAVE___SYNC_ADD_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_ADD_8 1
+| #define HAVE___SYNC_ADD_AND_FETCH_8 1
+| #define HAVE___SYNC_FETCH_AND_OR 1
+| #define HAVE___SYNC_OR_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_OR_8 1
+| #define HAVE___SYNC_OR_AND_FETCH_8 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+| #define HAVE___SYNC_LOCK_RELEASE_SET 1
+| #define HAVE_CXX_AUTO 1
+| #define HAVE_CXX_CONSTEXPR 1
+| #define HAVE_CXX_STATIC_ASSERT 1
+| #define HAVE_CXX_RVALUE_REFERENCES 1
+| #define HAVE_CXX_TEMPLATE_ALIAS 1
+| #define HAVE_TYPE_TRAITS 1
+| #define HAVE_STD_HASH 1
+| #define HAVE___HAS_TRIVIAL_COPY 1
+| #define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+| #define HAVE_STD_IS_RVALUE_REFERENCE 1
+| /* end confdefs.h.  */
+| 
+| /* Override any GCC internal prototype to avoid an error.
+|    Use char because int might match the return type of a GCC
+|    builtin and then its argument prototype would still apply.  */
+| #ifdef __cplusplus
+| extern "C"
+| #endif
+| char malloc ();
+| int
+| main ()
+| {
+| return malloc ();
+|   ;
+|   return 0;
+| }
+configure:5371: result: no
+configure:5379: checking for mallctl in -ljemalloc
+configure:5404: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -ljemalloc  -lnuma  >&5
+configure:5404: $? = 0
+configure:5413: result: yes
+configure:5421: checking for tc_malloc in -ltcmalloc_minimal
+configure:5446: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -ltcmalloc_minimal  -lnuma  >&5
+configure:5446: $? = 0
+configure:5455: result: yes
+configure:5463: checking for _Z16getMainHoardHeapv in -lhoard
+configure:5488: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -lhoard  -lnuma  >&5
+/usr/bin/ld: cannot find -lhoard
+collect2: error: ld returned 1 exit status
+configure:5488: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| #define HAVE_SYS_EPOLL_H 1
+| #define HAVE_NUMA_H 1
+| #define HAVE_LIBNUMA 1
+| #define HAVE___BUILTIN_CLZ 1
+| #define HAVE___BUILTIN_CLZL 1
+| #define HAVE___BUILTIN_CLZLL 1
+| #define HAVE___BUILTIN_CTZ 1
+| #define HAVE___BUILTIN_CTZL 1
+| #define HAVE___BUILTIN_CTZLL 1
+| #define HAVE___SYNC_SYNCHRONIZE 1
+| #define HAVE___SYNC_FETCH_AND_ADD 1
+| #define HAVE___SYNC_ADD_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_ADD_8 1
+| #define HAVE___SYNC_ADD_AND_FETCH_8 1
+| #define HAVE___SYNC_FETCH_AND_OR 1
+| #define HAVE___SYNC_OR_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_OR_8 1
+| #define HAVE___SYNC_OR_AND_FETCH_8 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+| #define HAVE___SYNC_LOCK_RELEASE_SET 1
+| #define HAVE_CXX_AUTO 1
+| #define HAVE_CXX_CONSTEXPR 1
+| #define HAVE_CXX_STATIC_ASSERT 1
+| #define HAVE_CXX_RVALUE_REFERENCES 1
+| #define HAVE_CXX_TEMPLATE_ALIAS 1
+| #define HAVE_TYPE_TRAITS 1
+| #define HAVE_STD_HASH 1
+| #define HAVE___HAS_TRIVIAL_COPY 1
+| #define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+| #define HAVE_STD_IS_RVALUE_REFERENCE 1
+| /* end confdefs.h.  */
+| 
+| /* Override any GCC internal prototype to avoid an error.
+|    Use char because int might match the return type of a GCC
+|    builtin and then its argument prototype would still apply.  */
+| #ifdef __cplusplus
+| extern "C"
+| #endif
+| char _Z16getMainHoardHeapv ();
+| int
+| main ()
+| {
+| return _Z16getMainHoardHeapv ();
+|   ;
+|   return 0;
+| }
+configure:5497: result: no
+configure:5537: checking for malloc library
+configure:5551: result: -ljemalloc
+configure:5580: checking whether off_t and long are the same type
+configure:5597: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+conftest.cpp: In function 'int f(long int)':
+conftest.cpp:57:30: error: redefinition of 'int f(long int)'
+ int f(off_t) {return 0;} int f(long) {return 0;}
+                              ^
+conftest.cpp:57:5: note: 'int f(off_t)' previously defined here
+ int f(off_t) {return 0;} int f(long) {return 0;}
+     ^
+configure:5597: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| #define HAVE_SYS_EPOLL_H 1
+| #define HAVE_NUMA_H 1
+| #define HAVE_LIBNUMA 1
+| #define HAVE___BUILTIN_CLZ 1
+| #define HAVE___BUILTIN_CLZL 1
+| #define HAVE___BUILTIN_CLZLL 1
+| #define HAVE___BUILTIN_CTZ 1
+| #define HAVE___BUILTIN_CTZL 1
+| #define HAVE___BUILTIN_CTZLL 1
+| #define HAVE___SYNC_SYNCHRONIZE 1
+| #define HAVE___SYNC_FETCH_AND_ADD 1
+| #define HAVE___SYNC_ADD_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_ADD_8 1
+| #define HAVE___SYNC_ADD_AND_FETCH_8 1
+| #define HAVE___SYNC_FETCH_AND_OR 1
+| #define HAVE___SYNC_OR_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_OR_8 1
+| #define HAVE___SYNC_OR_AND_FETCH_8 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+| #define HAVE___SYNC_LOCK_RELEASE_SET 1
+| #define HAVE_CXX_AUTO 1
+| #define HAVE_CXX_CONSTEXPR 1
+| #define HAVE_CXX_STATIC_ASSERT 1
+| #define HAVE_CXX_RVALUE_REFERENCES 1
+| #define HAVE_CXX_TEMPLATE_ALIAS 1
+| #define HAVE_TYPE_TRAITS 1
+| #define HAVE_STD_HASH 1
+| #define HAVE___HAS_TRIVIAL_COPY 1
+| #define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+| #define HAVE_STD_IS_RVALUE_REFERENCE 1
+| #define HAVE_JEMALLOC 1
+| /* end confdefs.h.  */
+| #include <stdio.h>
+| int f(off_t) {return 0;} int f(long) {return 0;}
+| int
+| main ()
+| {
+| 
+|   ;
+|   return 0;
+| }
+configure:5604: result: yes
+configure:5615: checking whether off_t and long long are the same type
+configure:5632: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5632: $? = 0
+configure:5639: result: no
+configure:5650: checking whether int64_t and long are the same type
+configure:5667: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+conftest.cpp: In function 'int f(long int)':
+conftest.cpp:58:32: error: redefinition of 'int f(long int)'
+ int f(int64_t) {return 0;} int f(long) {return 0;}
+                                ^
+conftest.cpp:58:5: note: 'int f(int64_t)' previously defined here
+ int f(int64_t) {return 0;} int f(long) {return 0;}
+     ^
+configure:5667: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| #define HAVE_SYS_EPOLL_H 1
+| #define HAVE_NUMA_H 1
+| #define HAVE_LIBNUMA 1
+| #define HAVE___BUILTIN_CLZ 1
+| #define HAVE___BUILTIN_CLZL 1
+| #define HAVE___BUILTIN_CLZLL 1
+| #define HAVE___BUILTIN_CTZ 1
+| #define HAVE___BUILTIN_CTZL 1
+| #define HAVE___BUILTIN_CTZLL 1
+| #define HAVE___SYNC_SYNCHRONIZE 1
+| #define HAVE___SYNC_FETCH_AND_ADD 1
+| #define HAVE___SYNC_ADD_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_ADD_8 1
+| #define HAVE___SYNC_ADD_AND_FETCH_8 1
+| #define HAVE___SYNC_FETCH_AND_OR 1
+| #define HAVE___SYNC_OR_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_OR_8 1
+| #define HAVE___SYNC_OR_AND_FETCH_8 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+| #define HAVE___SYNC_LOCK_RELEASE_SET 1
+| #define HAVE_CXX_AUTO 1
+| #define HAVE_CXX_CONSTEXPR 1
+| #define HAVE_CXX_STATIC_ASSERT 1
+| #define HAVE_CXX_RVALUE_REFERENCES 1
+| #define HAVE_CXX_TEMPLATE_ALIAS 1
+| #define HAVE_TYPE_TRAITS 1
+| #define HAVE_STD_HASH 1
+| #define HAVE___HAS_TRIVIAL_COPY 1
+| #define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+| #define HAVE_STD_IS_RVALUE_REFERENCE 1
+| #define HAVE_JEMALLOC 1
+| #define HAVE_OFF_T_IS_LONG 1
+| /* end confdefs.h.  */
+| #include <stdint.h>
+| int f(int64_t) {return 0;} int f(long) {return 0;}
+| int
+| main ()
+| {
+| 
+|   ;
+|   return 0;
+| }
+configure:5674: result: yes
+configure:5685: checking whether int64_t and long long are the same type
+configure:5702: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5702: $? = 0
+configure:5709: result: no
+configure:5720: checking whether size_t and unsigned are the same type
+configure:5737: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5737: $? = 0
+configure:5744: result: no
+configure:5755: checking whether size_t and unsigned long are the same type
+configure:5772: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+conftest.cpp: In function 'int f(long unsigned int)':
+conftest.cpp:59:31: error: redefinition of 'int f(long unsigned int)'
+ int f(size_t) {return 0;} int f(unsigned long) {return 0;}
+                               ^
+conftest.cpp:59:5: note: 'int f(size_t)' previously defined here
+ int f(size_t) {return 0;} int f(unsigned long) {return 0;}
+     ^
+configure:5772: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| #define HAVE_SYS_EPOLL_H 1
+| #define HAVE_NUMA_H 1
+| #define HAVE_LIBNUMA 1
+| #define HAVE___BUILTIN_CLZ 1
+| #define HAVE___BUILTIN_CLZL 1
+| #define HAVE___BUILTIN_CLZLL 1
+| #define HAVE___BUILTIN_CTZ 1
+| #define HAVE___BUILTIN_CTZL 1
+| #define HAVE___BUILTIN_CTZLL 1
+| #define HAVE___SYNC_SYNCHRONIZE 1
+| #define HAVE___SYNC_FETCH_AND_ADD 1
+| #define HAVE___SYNC_ADD_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_ADD_8 1
+| #define HAVE___SYNC_ADD_AND_FETCH_8 1
+| #define HAVE___SYNC_FETCH_AND_OR 1
+| #define HAVE___SYNC_OR_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_OR_8 1
+| #define HAVE___SYNC_OR_AND_FETCH_8 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+| #define HAVE___SYNC_LOCK_RELEASE_SET 1
+| #define HAVE_CXX_AUTO 1
+| #define HAVE_CXX_CONSTEXPR 1
+| #define HAVE_CXX_STATIC_ASSERT 1
+| #define HAVE_CXX_RVALUE_REFERENCES 1
+| #define HAVE_CXX_TEMPLATE_ALIAS 1
+| #define HAVE_TYPE_TRAITS 1
+| #define HAVE_STD_HASH 1
+| #define HAVE___HAS_TRIVIAL_COPY 1
+| #define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+| #define HAVE_STD_IS_RVALUE_REFERENCE 1
+| #define HAVE_JEMALLOC 1
+| #define HAVE_OFF_T_IS_LONG 1
+| #define HAVE_INT64_T_IS_LONG 1
+| /* end confdefs.h.  */
+| #include <stdio.h>
+| int f(size_t) {return 0;} int f(unsigned long) {return 0;}
+| int
+| main ()
+| {
+| 
+|   ;
+|   return 0;
+| }
+configure:5779: result: yes
+configure:5790: checking whether size_t and unsigned long long are the same type
+configure:5807: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5807: $? = 0
+configure:5814: result: no
+configure:5824: checking for long long
+configure:5824: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:5824: $? = 0
+configure:5824: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+conftest.cpp: In function 'int main()':
+conftest.cpp:95:14: error: expected primary-expression before 'long'
+ if (sizeof ((long long)))
+              ^
+conftest.cpp:95:14: error: expected ')' before 'long'
+conftest.cpp:96:14: error: expected ')' before ';' token
+      return 0;
+              ^
+conftest.cpp:96:14: error: expected ')' before ';' token
+configure:5824: $? = 1
+configure: failed program was:
+| /* confdefs.h */
+| #define PACKAGE_NAME "masstree-beta"
+| #define PACKAGE_TARNAME "masstree-beta"
+| #define PACKAGE_VERSION "0.1"
+| #define PACKAGE_STRING "masstree-beta 0.1"
+| #define PACKAGE_BUGREPORT ""
+| #define PACKAGE_URL ""
+| #define WORDS_BIGENDIAN_SET 1
+| #define STDC_HEADERS 1
+| #define HAVE_SYS_TYPES_H 1
+| #define HAVE_SYS_STAT_H 1
+| #define HAVE_STDLIB_H 1
+| #define HAVE_STRING_H 1
+| #define HAVE_MEMORY_H 1
+| #define HAVE_STRINGS_H 1
+| #define HAVE_INTTYPES_H 1
+| #define HAVE_STDINT_H 1
+| #define HAVE_UNISTD_H 1
+| #define HAVE_SYS_EPOLL_H 1
+| #define HAVE_NUMA_H 1
+| #define HAVE_LIBNUMA 1
+| #define HAVE___BUILTIN_CLZ 1
+| #define HAVE___BUILTIN_CLZL 1
+| #define HAVE___BUILTIN_CLZLL 1
+| #define HAVE___BUILTIN_CTZ 1
+| #define HAVE___BUILTIN_CTZL 1
+| #define HAVE___BUILTIN_CTZLL 1
+| #define HAVE___SYNC_SYNCHRONIZE 1
+| #define HAVE___SYNC_FETCH_AND_ADD 1
+| #define HAVE___SYNC_ADD_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_ADD_8 1
+| #define HAVE___SYNC_ADD_AND_FETCH_8 1
+| #define HAVE___SYNC_FETCH_AND_OR 1
+| #define HAVE___SYNC_OR_AND_FETCH 1
+| #define HAVE___SYNC_FETCH_AND_OR_8 1
+| #define HAVE___SYNC_OR_AND_FETCH_8 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+| #define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET 1
+| #define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+| #define HAVE___SYNC_LOCK_RELEASE_SET 1
+| #define HAVE_CXX_AUTO 1
+| #define HAVE_CXX_CONSTEXPR 1
+| #define HAVE_CXX_STATIC_ASSERT 1
+| #define HAVE_CXX_RVALUE_REFERENCES 1
+| #define HAVE_CXX_TEMPLATE_ALIAS 1
+| #define HAVE_TYPE_TRAITS 1
+| #define HAVE_STD_HASH 1
+| #define HAVE___HAS_TRIVIAL_COPY 1
+| #define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+| #define HAVE_STD_IS_RVALUE_REFERENCE 1
+| #define HAVE_JEMALLOC 1
+| #define HAVE_OFF_T_IS_LONG 1
+| #define HAVE_INT64_T_IS_LONG 1
+| #define HAVE_SIZE_T_IS_UNSIGNED_LONG 1
+| /* end confdefs.h.  */
+| #include <stdio.h>
+| #ifdef HAVE_SYS_TYPES_H
+| # include <sys/types.h>
+| #endif
+| #ifdef HAVE_SYS_STAT_H
+| # include <sys/stat.h>
+| #endif
+| #ifdef STDC_HEADERS
+| # include <stdlib.h>
+| # include <stddef.h>
+| #else
+| # ifdef HAVE_STDLIB_H
+| #  include <stdlib.h>
+| # endif
+| #endif
+| #ifdef HAVE_STRING_H
+| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+| #  include <memory.h>
+| # endif
+| # include <string.h>
+| #endif
+| #ifdef HAVE_STRINGS_H
+| # include <strings.h>
+| #endif
+| #ifdef HAVE_INTTYPES_H
+| # include <inttypes.h>
+| #endif
+| #ifdef HAVE_STDINT_H
+| # include <stdint.h>
+| #endif
+| #ifdef HAVE_UNISTD_H
+| # include <unistd.h>
+| #endif
+| int
+| main ()
+| {
+| if (sizeof ((long long)))
+|          return 0;
+|   ;
+|   return 0;
+| }
+configure:5824: result: yes
+configure:5838: checking size of short
+configure:5843: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:5843: $? = 0
+configure:5843: ./conftest
+configure:5843: $? = 0
+configure:5857: result: 2
+configure:5871: checking size of int
+configure:5876: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:5876: $? = 0
+configure:5876: ./conftest
+configure:5876: $? = 0
+configure:5890: result: 4
+configure:5904: checking size of long
+configure:5909: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:5909: $? = 0
+configure:5909: ./conftest
+configure:5909: $? = 0
+configure:5923: result: 8
+configure:5937: checking size of long long
+configure:5942: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:5942: $? = 0
+configure:5942: ./conftest
+configure:5942: $? = 0
+configure:5956: result: 8
+configure:5970: checking size of void *
+configure:5975: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:5975: $? = 0
+configure:5975: ./conftest
+configure:5975: $? = 0
+configure:5989: result: 8
+configure:6000: checking whether getline is declared
+configure:6000: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:6000: $? = 0
+configure:6000: result: yes
+configure:6015: checking time.h usability
+configure:6015: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:6015: $? = 0
+configure:6015: result: yes
+configure:6015: checking time.h presence
+configure:6015: g++ -E  conftest.cpp
+configure:6015: $? = 0
+configure:6015: result: yes
+configure:6015: checking for time.h
+configure:6015: result: yes
+configure:6015: checking execinfo.h usability
+configure:6015: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:6015: $? = 0
+configure:6015: result: yes
+configure:6015: checking execinfo.h presence
+configure:6015: g++ -E  conftest.cpp
+configure:6015: $? = 0
+configure:6015: result: yes
+configure:6015: checking for execinfo.h
+configure:6015: result: yes
+configure:6025: checking whether clock_gettime is declared
+configure:6025: g++ -std=gnu++0x -c -g -O2  conftest.cpp >&5
+configure:6025: $? = 0
+configure:6025: result: yes
+configure:6039: checking for library containing clock_gettime
+configure:6070: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:6070: $? = 0
+configure:6087: result: none required
+configure:6097: checking for clock_gettime
+configure:6097: g++ -std=gnu++0x -o conftest -g -O2   conftest.cpp -lnuma  >&5
+configure:6097: $? = 0
+configure:6097: result: yes
+configure:6156: checking whether MADV_HUGEPAGE is supported
+configure:6172: g++ -E  conftest.cpp
+configure:6172: $? = 0
+configure:6178: result: yes
+configure:6186: checking whether MAP_HUGETLB is supported
+configure:6202: g++ -E  conftest.cpp
+configure:6202: $? = 0
+configure:6208: result: yes
+configure:6437: creating ./config.status
+
+## ---------------------- ##
+## Running config.status. ##
+## ---------------------- ##
+
+This file was extended by masstree-beta config.status 0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = 
+  CONFIG_HEADERS  = 
+  CONFIG_LINKS    = 
+  CONFIG_COMMANDS = 
+  $ ./config.status 
+
+on dw-10
+
+config.status:863: creating GNUmakefile
+config.status:863: creating config.h
+
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+
+ac_cv_c_bigendian=no
+ac_cv_c_compiler_gnu=yes
+ac_cv_cxx_auto=yes
+ac_cv_cxx_compiler_gnu=yes
+ac_cv_cxx_constexpr=yes
+ac_cv_cxx_rvalue_references=yes
+ac_cv_cxx_static_assert=yes
+ac_cv_cxx_template_alias=yes
+ac_cv_env_CCC_set=
+ac_cv_env_CCC_value=
+ac_cv_env_CC_set=
+ac_cv_env_CC_value=
+ac_cv_env_CFLAGS_set=
+ac_cv_env_CFLAGS_value=
+ac_cv_env_CPPFLAGS_set=
+ac_cv_env_CPPFLAGS_value=
+ac_cv_env_CXXCPP_set=
+ac_cv_env_CXXCPP_value=
+ac_cv_env_CXXFLAGS_set=
+ac_cv_env_CXXFLAGS_value=
+ac_cv_env_CXX_set=
+ac_cv_env_CXX_value=
+ac_cv_env_LDFLAGS_set=
+ac_cv_env_LDFLAGS_value=
+ac_cv_env_LIBS_set=
+ac_cv_env_LIBS_value=
+ac_cv_env_build_alias_set=
+ac_cv_env_build_alias_value=
+ac_cv_env_host_alias_set=
+ac_cv_env_host_alias_value=
+ac_cv_env_target_alias_set=
+ac_cv_env_target_alias_value=
+ac_cv_func_clock_gettime=yes
+ac_cv_have___builtin_clz=yes
+ac_cv_have___builtin_clzl=yes
+ac_cv_have___builtin_clzll=yes
+ac_cv_have___builtin_ctz=yes
+ac_cv_have___builtin_ctzl=yes
+ac_cv_have___builtin_ctzll=yes
+ac_cv_have___has_trivial_copy=yes
+ac_cv_have___sync_add_and_fetch=yes
+ac_cv_have___sync_add_and_fetch_8=yes
+ac_cv_have___sync_bool_compare_and_swap=yes
+ac_cv_have___sync_bool_compare_and_swap_8=yes
+ac_cv_have___sync_fetch_and_add=yes
+ac_cv_have___sync_fetch_and_add_8=yes
+ac_cv_have___sync_fetch_and_or=yes
+ac_cv_have___sync_fetch_and_or_8=yes
+ac_cv_have___sync_lock_release_set=yes
+ac_cv_have___sync_lock_test_and_set=yes
+ac_cv_have___sync_lock_test_and_set_val=yes
+ac_cv_have___sync_or_and_fetch=yes
+ac_cv_have___sync_or_and_fetch_8=yes
+ac_cv_have___sync_synchronize=yes
+ac_cv_have___sync_val_compare_and_swap=yes
+ac_cv_have___sync_val_compare_and_swap_8=yes
+ac_cv_have_decl_clock_gettime=yes
+ac_cv_have_decl_getline=yes
+ac_cv_have_same_type_int64_t_is_long=yes
+ac_cv_have_same_type_int64_t_is_long_long=no
+ac_cv_have_same_type_off_t_is_long=yes
+ac_cv_have_same_type_off_t_is_long_long=no
+ac_cv_have_same_type_size_t_is_unsigned=no
+ac_cv_have_same_type_size_t_is_unsigned_long=yes
+ac_cv_have_same_type_size_t_is_unsigned_long_long=no
+ac_cv_have_std_hash=yes
+ac_cv_have_std_is_rvalue_reference=yes
+ac_cv_have_std_is_trivially_copyable=yes
+ac_cv_header_execinfo_h=yes
+ac_cv_header_inttypes_h=yes
+ac_cv_header_memory_h=yes
+ac_cv_header_numa_h=yes
+ac_cv_header_stdc=yes
+ac_cv_header_stdint_h=yes
+ac_cv_header_stdlib_h=yes
+ac_cv_header_string_h=yes
+ac_cv_header_strings_h=yes
+ac_cv_header_sys_epoll_h=yes
+ac_cv_header_sys_stat_h=yes
+ac_cv_header_sys_types_h=yes
+ac_cv_header_time_h=yes
+ac_cv_header_type_traits=yes
+ac_cv_header_unistd_h=yes
+ac_cv_lib_flow_malloc=no
+ac_cv_lib_hoard__Z16getMainHoardHeapv=no
+ac_cv_lib_jemalloc_mallctl=yes
+ac_cv_lib_tcmalloc_minimal_tc_malloc=yes
+ac_cv_max_key_len=1024
+ac_cv_objext=o
+ac_cv_path_EGREP='/bin/grep -E'
+ac_cv_path_GREP=/bin/grep
+ac_cv_prog_CXXCPP='g++ -E'
+ac_cv_prog_ac_ct_CC=gcc
+ac_cv_prog_ac_ct_CXX=g++
+ac_cv_prog_cc_c89=
+ac_cv_prog_cc_g=yes
+ac_cv_prog_cxx_g=yes
+ac_cv_row_type=bag
+ac_cv_search_clock_gettime='none required'
+ac_cv_search_numa_available=-lnuma
+ac_cv_sizeof_int=4
+ac_cv_sizeof_long=8
+ac_cv_sizeof_long_long=8
+ac_cv_sizeof_short=2
+ac_cv_sizeof_void_p=8
+ac_cv_std_move=yes
+ac_cv_type_long_long=yes
+
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+
+CC='gcc'
+CFLAGS='-g -O2'
+CPPFLAGS=''
+CXX='g++ -std=gnu++0x'
+CXXCPP='g++ -E'
+CXXFLAGS='-g -O2'
+DEFS='-DHAVE_CONFIG_H'
+ECHO_C=''
+ECHO_N='-n'
+ECHO_T=''
+EGREP='/bin/grep -E'
+EXEEXT=''
+GREP='/bin/grep'
+LDFLAGS=''
+LIBOBJS=''
+LIBS='-lnuma '
+LTLIBOBJS=''
+MALLOC_LIBS='-ljemalloc'
+OBJEXT='o'
+PACKAGE_BUGREPORT=''
+PACKAGE_NAME='masstree-beta'
+PACKAGE_STRING='masstree-beta 0.1'
+PACKAGE_TARNAME='masstree-beta'
+PACKAGE_URL=''
+PACKAGE_VERSION='0.1'
+PATH_SEPARATOR=':'
+SHELL='/bin/bash'
+ac_configure_args=' '\''--enable-max-key-len=1024'\'' '\''--disable-assertions'\'' '\''--enable-invariants'\'' '\''--enable-preconditions'\'' '\''--with-malloc=jemalloc'\'''
+ac_ct_CC='gcc'
+ac_ct_CXX='g++'
+bindir='${exec_prefix}/bin'
+build_alias=''
+datadir='${datarootdir}'
+datarootdir='${prefix}/share'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+dvidir='${docdir}'
+exec_prefix='${prefix}'
+host_alias=''
+htmldir='${docdir}'
+includedir='${prefix}/include'
+infodir='${datarootdir}/info'
+libdir='${exec_prefix}/lib'
+libexecdir='${exec_prefix}/libexec'
+localedir='${datarootdir}/locale'
+localstatedir='${prefix}/var'
+mandir='${datarootdir}/man'
+oldincludedir='/usr/include'
+pdfdir='${docdir}'
+prefix='/usr/local'
+program_transform_name='s,x,x,'
+psdir='${docdir}'
+runstatedir='${localstatedir}/run'
+sbindir='${exec_prefix}/sbin'
+sharedstatedir='${prefix}/com'
+sysconfdir='${prefix}/etc'
+target_alias=''
+
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+
+/* confdefs.h */
+#define PACKAGE_NAME "masstree-beta"
+#define PACKAGE_TARNAME "masstree-beta"
+#define PACKAGE_VERSION "0.1"
+#define PACKAGE_STRING "masstree-beta 0.1"
+#define PACKAGE_BUGREPORT ""
+#define PACKAGE_URL ""
+#define WORDS_BIGENDIAN_SET 1
+#define STDC_HEADERS 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRING_H 1
+#define HAVE_MEMORY_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_UNISTD_H 1
+#define HAVE_SYS_EPOLL_H 1
+#define HAVE_NUMA_H 1
+#define HAVE_LIBNUMA 1
+#define HAVE___BUILTIN_CLZ 1
+#define HAVE___BUILTIN_CLZL 1
+#define HAVE___BUILTIN_CLZLL 1
+#define HAVE___BUILTIN_CTZ 1
+#define HAVE___BUILTIN_CTZL 1
+#define HAVE___BUILTIN_CTZLL 1
+#define HAVE___SYNC_SYNCHRONIZE 1
+#define HAVE___SYNC_FETCH_AND_ADD 1
+#define HAVE___SYNC_ADD_AND_FETCH 1
+#define HAVE___SYNC_FETCH_AND_ADD_8 1
+#define HAVE___SYNC_ADD_AND_FETCH_8 1
+#define HAVE___SYNC_FETCH_AND_OR 1
+#define HAVE___SYNC_OR_AND_FETCH 1
+#define HAVE___SYNC_FETCH_AND_OR_8 1
+#define HAVE___SYNC_OR_AND_FETCH_8 1
+#define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+#define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+#define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+#define HAVE___SYNC_LOCK_TEST_AND_SET 1
+#define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1
+#define HAVE___SYNC_LOCK_RELEASE_SET 1
+#define HAVE_CXX_AUTO 1
+#define HAVE_CXX_CONSTEXPR 1
+#define HAVE_CXX_STATIC_ASSERT 1
+#define HAVE_CXX_RVALUE_REFERENCES 1
+#define HAVE_CXX_TEMPLATE_ALIAS 1
+#define HAVE_TYPE_TRAITS 1
+#define HAVE_STD_HASH 1
+#define HAVE___HAS_TRIVIAL_COPY 1
+#define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+#define HAVE_STD_IS_RVALUE_REFERENCE 1
+#define HAVE_JEMALLOC 1
+#define HAVE_OFF_T_IS_LONG 1
+#define HAVE_INT64_T_IS_LONG 1
+#define HAVE_SIZE_T_IS_UNSIGNED_LONG 1
+#define HAVE_LONG_LONG 1
+#define SIZEOF_SHORT 2
+#define SIZEOF_INT 4
+#define SIZEOF_LONG 8
+#define SIZEOF_LONG_LONG 8
+#define SIZEOF_VOID_P 8
+#define HAVE_DECL_GETLINE 1
+#define HAVE_TIME_H 1
+#define HAVE_EXECINFO_H 1
+#define HAVE_DECL_CLOCK_GETTIME 1
+#define HAVE_CLOCK_GETTIME 1
+#define MASSTREE_ROW_TYPE_BAG 1
+#define MASSTREE_MAXKEYLEN 1024
+#define HAVE_MADV_HUGEPAGE 1
+#define HAVE_MAP_HUGETLB 1
+#define HAVE_SUPERPAGE 1
+#define ENABLE_PRECONDITIONS 1
+#define ENABLE_INVARIANTS 1
+#define CACHE_LINE_SIZE 64
+#define HAVE_UNALIGNED_ACCESS 1
+
+configure: exit 0
diff --git a/silo/masstree/config.status b/silo/masstree/config.status
new file mode 100755 (executable)
index 0000000..8eec13f
--- /dev/null
@@ -0,0 +1,1053 @@
+#! /bin/bash
+# Generated by configure.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=${CONFIG_SHELL-/bin/bash}
+export SHELL
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by masstree-beta $as_me 0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+# Files that config.status was made for.
+config_files=" GNUmakefile"
+config_headers=" config.h"
+
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to the package provider."
+
+ac_cs_config="'--enable-max-key-len=1024' '--disable-assertions' '--enable-invariants' '--enable-preconditions' '--with-malloc=jemalloc'"
+ac_cs_version="\
+masstree-beta config.status 0.1
+configured by ./configure, generated by GNU Autoconf 2.69,
+  with options \"$ac_cs_config\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='/home/ahmad/benchmark-fuzzying-tool/silo/masstree'
+srcdir='.'
+test -n "$AWK" || AWK=awk
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+if $ac_cs_recheck; then
+  set X /bin/bash './configure'  '--enable-max-key-len=1024' '--disable-assertions' '--enable-invariants' '--enable-preconditions' '--with-malloc=jemalloc' $ac_configure_extra_args --no-create --no-recursion
+  shift
+  $as_echo "running CONFIG_SHELL=/bin/bash $*" >&6
+  CONFIG_SHELL='/bin/bash'
+  export CONFIG_SHELL
+  exec "$@"
+fi
+
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "GNUmakefile") CONFIG_FILES="$CONFIG_FILES GNUmakefile" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+cat >>"$ac_tmp/subs1.awk" <<\_ACAWK &&
+S["LTLIBOBJS"]=""
+S["LIBOBJS"]=""
+S["MALLOC_LIBS"]="-ljemalloc"
+S["EGREP"]="/bin/grep -E"
+S["GREP"]="/bin/grep"
+S["CXXCPP"]="g++ -E"
+S["ac_ct_CXX"]="g++"
+S["CXXFLAGS"]="-g -O2"
+S["CXX"]="g++ -std=gnu++0x"
+S["OBJEXT"]="o"
+S["EXEEXT"]=""
+S["ac_ct_CC"]="gcc"
+S["CPPFLAGS"]=""
+S["LDFLAGS"]=""
+S["CFLAGS"]="-g -O2"
+S["CC"]="gcc"
+S["ac_configure_args"]=" '--enable-max-key-len=1024' '--disable-assertions' '--enable-invariants' '--enable-preconditions' '--with-malloc=jemalloc'"
+S["target_alias"]=""
+S["host_alias"]=""
+S["build_alias"]=""
+S["LIBS"]="-lnuma "
+S["ECHO_T"]=""
+S["ECHO_N"]="-n"
+S["ECHO_C"]=""
+S["DEFS"]="-DHAVE_CONFIG_H"
+S["mandir"]="${datarootdir}/man"
+S["localedir"]="${datarootdir}/locale"
+S["libdir"]="${exec_prefix}/lib"
+S["psdir"]="${docdir}"
+S["pdfdir"]="${docdir}"
+S["dvidir"]="${docdir}"
+S["htmldir"]="${docdir}"
+S["infodir"]="${datarootdir}/info"
+S["docdir"]="${datarootdir}/doc/${PACKAGE_TARNAME}"
+S["oldincludedir"]="/usr/include"
+S["includedir"]="${prefix}/include"
+S["runstatedir"]="${localstatedir}/run"
+S["localstatedir"]="${prefix}/var"
+S["sharedstatedir"]="${prefix}/com"
+S["sysconfdir"]="${prefix}/etc"
+S["datadir"]="${datarootdir}"
+S["datarootdir"]="${prefix}/share"
+S["libexecdir"]="${exec_prefix}/libexec"
+S["sbindir"]="${exec_prefix}/sbin"
+S["bindir"]="${exec_prefix}/bin"
+S["program_transform_name"]="s,x,x,"
+S["prefix"]="/usr/local"
+S["exec_prefix"]="${prefix}"
+S["PACKAGE_URL"]=""
+S["PACKAGE_BUGREPORT"]=""
+S["PACKAGE_STRING"]="masstree-beta 0.1"
+S["PACKAGE_VERSION"]="0.1"
+S["PACKAGE_TARNAME"]="masstree-beta"
+S["PACKAGE_NAME"]="masstree-beta"
+S["PATH_SEPARATOR"]=":"
+S["SHELL"]="/bin/bash"
+_ACAWK
+cat >>"$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+D["PACKAGE_NAME"]=" \"masstree-beta\""
+D["PACKAGE_TARNAME"]=" \"masstree-beta\""
+D["PACKAGE_VERSION"]=" \"0.1\""
+D["PACKAGE_STRING"]=" \"masstree-beta 0.1\""
+D["PACKAGE_BUGREPORT"]=" \"\""
+D["PACKAGE_URL"]=" \"\""
+D["WORDS_BIGENDIAN_SET"]=" 1"
+D["STDC_HEADERS"]=" 1"
+D["HAVE_SYS_TYPES_H"]=" 1"
+D["HAVE_SYS_STAT_H"]=" 1"
+D["HAVE_STDLIB_H"]=" 1"
+D["HAVE_STRING_H"]=" 1"
+D["HAVE_MEMORY_H"]=" 1"
+D["HAVE_STRINGS_H"]=" 1"
+D["HAVE_INTTYPES_H"]=" 1"
+D["HAVE_STDINT_H"]=" 1"
+D["HAVE_UNISTD_H"]=" 1"
+D["HAVE_SYS_EPOLL_H"]=" 1"
+D["HAVE_NUMA_H"]=" 1"
+D["HAVE_LIBNUMA"]=" 1"
+D["HAVE___BUILTIN_CLZ"]=" 1"
+D["HAVE___BUILTIN_CLZL"]=" 1"
+D["HAVE___BUILTIN_CLZLL"]=" 1"
+D["HAVE___BUILTIN_CTZ"]=" 1"
+D["HAVE___BUILTIN_CTZL"]=" 1"
+D["HAVE___BUILTIN_CTZLL"]=" 1"
+D["HAVE___SYNC_SYNCHRONIZE"]=" 1"
+D["HAVE___SYNC_FETCH_AND_ADD"]=" 1"
+D["HAVE___SYNC_ADD_AND_FETCH"]=" 1"
+D["HAVE___SYNC_FETCH_AND_ADD_8"]=" 1"
+D["HAVE___SYNC_ADD_AND_FETCH_8"]=" 1"
+D["HAVE___SYNC_FETCH_AND_OR"]=" 1"
+D["HAVE___SYNC_OR_AND_FETCH"]=" 1"
+D["HAVE___SYNC_FETCH_AND_OR_8"]=" 1"
+D["HAVE___SYNC_OR_AND_FETCH_8"]=" 1"
+D["HAVE___SYNC_BOOL_COMPARE_AND_SWAP"]=" 1"
+D["HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8"]=" 1"
+D["HAVE___SYNC_VAL_COMPARE_AND_SWAP"]=" 1"
+D["HAVE___SYNC_VAL_COMPARE_AND_SWAP_8"]=" 1"
+D["HAVE___SYNC_LOCK_TEST_AND_SET"]=" 1"
+D["HAVE___SYNC_LOCK_TEST_AND_SET_VAL"]=" 1"
+D["HAVE___SYNC_LOCK_RELEASE_SET"]=" 1"
+D["HAVE_CXX_AUTO"]=" 1"
+D["HAVE_CXX_CONSTEXPR"]=" 1"
+D["HAVE_CXX_STATIC_ASSERT"]=" 1"
+D["HAVE_CXX_RVALUE_REFERENCES"]=" 1"
+D["HAVE_CXX_TEMPLATE_ALIAS"]=" 1"
+D["HAVE_TYPE_TRAITS"]=" 1"
+D["HAVE_STD_HASH"]=" 1"
+D["HAVE___HAS_TRIVIAL_COPY"]=" 1"
+D["HAVE_STD_IS_TRIVIALLY_COPYABLE"]=" 1"
+D["HAVE_STD_IS_RVALUE_REFERENCE"]=" 1"
+D["HAVE_JEMALLOC"]=" 1"
+D["HAVE_OFF_T_IS_LONG"]=" 1"
+D["HAVE_INT64_T_IS_LONG"]=" 1"
+D["HAVE_SIZE_T_IS_UNSIGNED_LONG"]=" 1"
+D["HAVE_LONG_LONG"]=" 1"
+D["SIZEOF_SHORT"]=" 2"
+D["SIZEOF_INT"]=" 4"
+D["SIZEOF_LONG"]=" 8"
+D["SIZEOF_LONG_LONG"]=" 8"
+D["SIZEOF_VOID_P"]=" 8"
+D["HAVE_DECL_GETLINE"]=" 1"
+D["HAVE_TIME_H"]=" 1"
+D["HAVE_EXECINFO_H"]=" 1"
+D["HAVE_DECL_CLOCK_GETTIME"]=" 1"
+D["HAVE_CLOCK_GETTIME"]=" 1"
+D["MASSTREE_ROW_TYPE_BAG"]=" 1"
+D["MASSTREE_MAXKEYLEN"]=" 1024"
+D["HAVE_MADV_HUGEPAGE"]=" 1"
+D["HAVE_MAP_HUGETLB"]=" 1"
+D["HAVE_SUPERPAGE"]=" 1"
+D["ENABLE_PRECONDITIONS"]=" 1"
+D["ENABLE_INVARIANTS"]=" 1"
+D["CACHE_LINE_SIZE"]=" 64"
+D["HAVE_UNALIGNED_ACCESS"]=" 1"
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+[_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ][_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]*([\t (]|$)/ {
+  line = $ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+  ac_datarootdir_hack='
+  s&@datadir@&${datarootdir}&g
+  s&@docdir@&${datarootdir}/doc/${PACKAGE_TARNAME}&g
+  s&@infodir@&${datarootdir}/info&g
+  s&@localedir@&${datarootdir}/locale&g
+  s&@mandir@&${datarootdir}/man&g
+  s&\${datarootdir}&${prefix}/share&g' ;;
+esac
+ac_sed_extra="/^[       ]*VPATH[        ]*=[    ]*/{
+h
+s///
+s/^/:/
+s/[     ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[  ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[      ]*$//
+}
+
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
diff --git a/silo/masstree/configure b/silo/masstree/configure
new file mode 100755 (executable)
index 0000000..9a7c6a7
--- /dev/null
@@ -0,0 +1,7594 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for masstree-beta 0.1.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+        /*)
+          for as_base in sh bash ksh sh5; do
+            # Try only shells that exist, to save several forks.
+            as_shell=$as_dir/$as_base
+            if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+                   { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+                  if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+          done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+             { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='masstree-beta'
+PACKAGE_TARNAME='masstree-beta'
+PACKAGE_VERSION='0.1'
+PACKAGE_STRING='masstree-beta 0.1'
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+MALLOC_LIBS
+EGREP
+GREP
+CXXCPP
+ac_ct_CXX
+CXXFLAGS
+CXX
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+ac_configure_args
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_malloc
+enable_row_type
+enable_max_key_len
+enable_superpage
+enable_memdebug
+enable_assert
+enable_assertions
+enable_preconditions
+enable_invariants
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CXX
+CXXFLAGS
+CCC
+CXXCPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir runstatedir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures masstree-beta 0.1 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/masstree-beta]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of masstree-beta 0.1:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-row-type=ARG   row type: bag array array_ver str, default bag
+  --enable-max-key-len=ARG
+                          maximum length of a key in bytes, default 255
+  --disable-superpage     disable superpage support
+  --enable-memdebug       enable memory debugging
+
+  --disable-assertions    disable debugging assertions
+  --disable-preconditions disable precondition assertions
+  --disable-invariants    disable invariant assertions
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-malloc=TYPE      memory allocator
+                          (malloc|jemalloc|tcmalloc|hoard|flow)
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  CXXCPP      C++ preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+masstree-beta configure 0.1
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_compile
+
+# ac_fn_cxx_try_run LINENO
+# ------------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_cxx_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_run
+
+# ac_fn_cxx_try_cpp LINENO
+# ------------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_cpp
+
+# ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES
+# ---------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_cxx_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_cxx_check_header_compile
+
+# ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES
+# ---------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_cxx_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_cxx_check_header_mongrel
+
+# ac_fn_cxx_try_link LINENO
+# -------------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_cxx_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_link
+
+# ac_fn_cxx_check_type LINENO TYPE VAR INCLUDES
+# ---------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_cxx_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+        return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+           return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_cxx_check_type
+
+# ac_fn_cxx_compute_int LINENO EXPR VAR INCLUDES
+# ----------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_cxx_compute_int ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if test "$cross_compiling" = yes; then
+    # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid; break
+else
+  as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+                       if test $ac_lo -le $ac_mid; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_lo=$ac_mid; break
+else
+  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+                       if test $ac_mid -le $ac_hi; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid
+else
+  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    return 1;
+  if (($2) < 0)
+    {
+      long int i = longval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%ld", i);
+    }
+  else
+    {
+      unsigned long int i = ulongval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%lu", i);
+    }
+  /* Do not output a trailing newline, as this causes \r\n confusion
+     on some platforms.  */
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+  echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+  ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+  fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_compute_int
+
+# ac_fn_cxx_check_decl LINENO SYMBOL VAR INCLUDES
+# -----------------------------------------------
+# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
+# accordingly.
+ac_fn_cxx_check_decl ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  as_decl_name=`echo $2|sed 's/ *(.*//'`
+  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+#ifndef $as_decl_name
+#ifdef __cplusplus
+  (void) $as_decl_use;
+#else
+  (void) $as_decl_name;
+#endif
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_cxx_check_decl
+
+# ac_fn_cxx_check_func LINENO FUNC VAR
+# ------------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_cxx_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_cxx_check_func
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by masstree-beta $as_me 0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+ac_config_files="$ac_config_files GNUmakefile"
+
+
+
+ac_user_cxx=${CXX+y}
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+  if test -n "$CCC"; then
+    CXX=$CCC
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CXX" && break
+done
+
+  if test "x$ac_ct_CXX" = x; then
+    CXX="g++"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CXX=$ac_ct_CXX
+  fi
+fi
+
+  fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+   ac_cxx_werror_flag=yes
+   ac_cv_prog_cxx_g=no
+   CXXFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+else
+  CXXFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+        CXXFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+
+$as_echo "#define WORDS_BIGENDIAN_SET 1" >>confdefs.h
+
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5
+$as_echo_n "checking how to run the C++ preprocessor... " >&6; }
+if test -z "$CXXCPP"; then
+  if ${ac_cv_prog_CXXCPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CXXCPP needs to be expanded
+    for CXXCPP in "$CXX -E" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CXXCPP=$CXXCPP
+
+fi
+  CXXCPP=$ac_cv_prog_CXXCPP
+else
+  ac_cv_prog_CXXCPP=$CXXCPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5
+$as_echo "$CXXCPP" >&6; }
+ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
+if ${ac_cv_c_bigendian+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_bigendian=unknown
+    # See if we're dealing with a universal compiler.
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifndef __APPLE_CC__
+              not a universal capable compiler
+            #endif
+            typedef int dummy;
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+       # Check for potential -arch flags.  It is not universal unless
+       # there are at least two -arch flags with different values.
+       ac_arch=
+       ac_prev=
+       for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
+        if test -n "$ac_prev"; then
+          case $ac_word in
+            i?86 | x86_64 | ppc | ppc64)
+              if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
+                ac_arch=$ac_word
+              else
+                ac_cv_c_bigendian=universal
+                break
+              fi
+              ;;
+          esac
+          ac_prev=
+        elif test "x$ac_word" = "x-arch"; then
+          ac_prev=arch
+        fi
+       done
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if sys/param.h defines the BYTE_ORDER macro.
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+            #include <sys/param.h>
+
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+                    && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+                    && LITTLE_ENDIAN)
+             bogus endian macros
+            #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+               #include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+                not big endian
+               #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+
+int
+main ()
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+             bogus endian macros
+            #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to _BIG_ENDIAN or not.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+
+int
+main ()
+{
+#ifndef _BIG_ENDIAN
+                not big endian
+               #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # Compile a test program.
+      if test "$cross_compiling" = yes; then :
+  # Try to guess by grepping values from an object file.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+short int ascii_mm[] =
+                 { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+               short int ascii_ii[] =
+                 { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+               int use_ascii (int i) {
+                 return ascii_mm[i] + ascii_ii[i];
+               }
+               short int ebcdic_ii[] =
+                 { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+               short int ebcdic_mm[] =
+                 { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+               int use_ebcdic (int i) {
+                 return ebcdic_mm[i] + ebcdic_ii[i];
+               }
+               extern int foo;
+
+int
+main ()
+{
+return use_ascii (foo) == use_ebcdic (foo);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+             ac_cv_c_bigendian=yes
+           fi
+           if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+             if test "$ac_cv_c_bigendian" = unknown; then
+               ac_cv_c_bigendian=no
+             else
+               # finding both strings is unlikely to happen, but who knows?
+               ac_cv_c_bigendian=unknown
+             fi
+           fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+
+            /* Are we little or big endian?  From Harbison&Steele.  */
+            union
+            {
+              long int l;
+              char c[sizeof (long int)];
+            } u;
+            u.l = 1;
+            return u.c[sizeof (long int) - 1] == 1;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+  ac_cv_c_bigendian=no
+else
+  ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+$as_echo "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+   yes)
+     $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h
+;; #(
+   no)
+      ;; #(
+   universal)
+
+$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+
+     ;; #(
+   *)
+     as_fn_error $? "unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
+ esac
+
+
+for ac_header in sys/epoll.h numa.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing numa_available" >&5
+$as_echo_n "checking for library containing numa_available... " >&6; }
+if ${ac_cv_search_numa_available+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char numa_available ();
+int
+main ()
+{
+return numa_available ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' numa; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_search_numa_available=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_numa_available+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_numa_available+:} false; then :
+
+else
+  ac_cv_search_numa_available=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_numa_available" >&5
+$as_echo "$ac_cv_search_numa_available" >&6; }
+ac_res=$ac_cv_search_numa_available
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+$as_echo "#define HAVE_LIBNUMA 1" >>confdefs.h
+
+fi
+
+
+
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clz builtin" >&5
+$as_echo_n "checking for __builtin_clz builtin... " >&6; }
+if ${ac_cv_have___builtin_clz+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned f(unsigned x) { return __builtin_clz(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_clz=yes
+else
+  ac_cv_have___builtin_clz=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_clz" >&5
+$as_echo "$ac_cv_have___builtin_clz" >&6; }
+    if test $ac_cv_have___builtin_clz = yes; then
+
+$as_echo "#define HAVE___BUILTIN_CLZ 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clzl builtin" >&5
+$as_echo_n "checking for __builtin_clzl builtin... " >&6; }
+if ${ac_cv_have___builtin_clzl+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long f(unsigned long x) { return __builtin_clzl(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_clzl=yes
+else
+  ac_cv_have___builtin_clzl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_clzl" >&5
+$as_echo "$ac_cv_have___builtin_clzl" >&6; }
+    if test $ac_cv_have___builtin_clzl = yes; then
+
+$as_echo "#define HAVE___BUILTIN_CLZL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clzll builtin" >&5
+$as_echo_n "checking for __builtin_clzll builtin... " >&6; }
+if ${ac_cv_have___builtin_clzll+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long long f(unsigned long long x) { return __builtin_clzll(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_clzll=yes
+else
+  ac_cv_have___builtin_clzll=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_clzll" >&5
+$as_echo "$ac_cv_have___builtin_clzll" >&6; }
+    if test $ac_cv_have___builtin_clzll = yes; then
+
+$as_echo "#define HAVE___BUILTIN_CLZLL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_ctz builtin" >&5
+$as_echo_n "checking for __builtin_ctz builtin... " >&6; }
+if ${ac_cv_have___builtin_ctz+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned f(unsigned x) { return __builtin_ctz(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_ctz=yes
+else
+  ac_cv_have___builtin_ctz=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_ctz" >&5
+$as_echo "$ac_cv_have___builtin_ctz" >&6; }
+    if test $ac_cv_have___builtin_ctz = yes; then
+
+$as_echo "#define HAVE___BUILTIN_CTZ 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_ctzl builtin" >&5
+$as_echo_n "checking for __builtin_ctzl builtin... " >&6; }
+if ${ac_cv_have___builtin_ctzl+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long f(unsigned long x) { return __builtin_ctzl(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_ctzl=yes
+else
+  ac_cv_have___builtin_ctzl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_ctzl" >&5
+$as_echo "$ac_cv_have___builtin_ctzl" >&6; }
+    if test $ac_cv_have___builtin_ctzl = yes; then
+
+$as_echo "#define HAVE___BUILTIN_CTZL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_ctzll builtin" >&5
+$as_echo_n "checking for __builtin_ctzll builtin... " >&6; }
+if ${ac_cv_have___builtin_ctzll+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+unsigned long long f(unsigned long long x) { return __builtin_ctzll(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___builtin_ctzll=yes
+else
+  ac_cv_have___builtin_ctzll=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___builtin_ctzll" >&5
+$as_echo "$ac_cv_have___builtin_ctzll" >&6; }
+    if test $ac_cv_have___builtin_ctzll = yes; then
+
+$as_echo "#define HAVE___BUILTIN_CTZLL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_synchronize builtin" >&5
+$as_echo_n "checking for __sync_synchronize builtin... " >&6; }
+if ${ac_cv_have___sync_synchronize+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long x = 11;
+    void f(long i) { long* y = &x; __sync_synchronize(); *y = i; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_synchronize=yes
+else
+  ac_cv_have___sync_synchronize=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_synchronize" >&5
+$as_echo "$ac_cv_have___sync_synchronize" >&6; }
+    if test $ac_cv_have___sync_synchronize = yes; then
+
+$as_echo "#define HAVE___SYNC_SYNCHRONIZE 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_add builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_add builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_add+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_fetch_and_add(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_add=yes
+else
+  ac_cv_have___sync_fetch_and_add=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_add" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_add" >&6; }
+    if test $ac_cv_have___sync_fetch_and_add = yes; then
+
+$as_echo "#define HAVE___SYNC_FETCH_AND_ADD 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_add_and_fetch builtin" >&5
+$as_echo_n "checking for __sync_add_and_fetch builtin... " >&6; }
+if ${ac_cv_have___sync_add_and_fetch+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_add_and_fetch(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_add_and_fetch=yes
+else
+  ac_cv_have___sync_add_and_fetch=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_add_and_fetch" >&5
+$as_echo "$ac_cv_have___sync_add_and_fetch" >&6; }
+    if test $ac_cv_have___sync_add_and_fetch = yes; then
+
+$as_echo "#define HAVE___SYNC_ADD_AND_FETCH 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_add_8 builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_add_8 builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_add_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_add(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_add_8=yes
+else
+  ac_cv_have___sync_fetch_and_add_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_add_8" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_add_8" >&6; }
+    if test $ac_cv_have___sync_fetch_and_add_8 = yes; then
+
+$as_echo "#define HAVE___SYNC_FETCH_AND_ADD_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_add_and_fetch_8 builtin" >&5
+$as_echo_n "checking for __sync_add_and_fetch_8 builtin... " >&6; }
+if ${ac_cv_have___sync_add_and_fetch_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_add_and_fetch(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_add_and_fetch_8=yes
+else
+  ac_cv_have___sync_add_and_fetch_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_add_and_fetch_8" >&5
+$as_echo "$ac_cv_have___sync_add_and_fetch_8" >&6; }
+    if test $ac_cv_have___sync_add_and_fetch_8 = yes; then
+
+$as_echo "#define HAVE___SYNC_ADD_AND_FETCH_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_or builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_or builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_or+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_fetch_and_or(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_or=yes
+else
+  ac_cv_have___sync_fetch_and_or=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_or" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_or" >&6; }
+    if test $ac_cv_have___sync_fetch_and_or = yes; then
+
+$as_echo "#define HAVE___SYNC_FETCH_AND_OR 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_or_and_fetch builtin" >&5
+$as_echo_n "checking for __sync_or_and_fetch builtin... " >&6; }
+if ${ac_cv_have___sync_or_and_fetch+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_or_and_fetch(x, 2L); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_or_and_fetch=yes
+else
+  ac_cv_have___sync_or_and_fetch=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_or_and_fetch" >&5
+$as_echo "$ac_cv_have___sync_or_and_fetch" >&6; }
+    if test $ac_cv_have___sync_or_and_fetch = yes; then
+
+$as_echo "#define HAVE___SYNC_OR_AND_FETCH 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_or_8 builtin" >&5
+$as_echo_n "checking for __sync_fetch_and_or_8 builtin... " >&6; }
+if ${ac_cv_have___sync_fetch_and_or_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_or(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_fetch_and_or_8=yes
+else
+  ac_cv_have___sync_fetch_and_or_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_fetch_and_or_8" >&5
+$as_echo "$ac_cv_have___sync_fetch_and_or_8" >&6; }
+    if test $ac_cv_have___sync_fetch_and_or_8 = yes; then
+
+$as_echo "#define HAVE___SYNC_FETCH_AND_OR_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_or_and_fetch_8 builtin" >&5
+$as_echo_n "checking for __sync_or_and_fetch_8 builtin... " >&6; }
+if ${ac_cv_have___sync_or_and_fetch_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_or_and_fetch(x, (int64_t) 2); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_or_and_fetch_8=yes
+else
+  ac_cv_have___sync_or_and_fetch_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_or_and_fetch_8" >&5
+$as_echo "$ac_cv_have___sync_or_and_fetch_8" >&6; }
+    if test $ac_cv_have___sync_or_and_fetch_8 = yes; then
+
+$as_echo "#define HAVE___SYNC_OR_AND_FETCH_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_bool_compare_and_swap builtin" >&5
+$as_echo_n "checking for __sync_bool_compare_and_swap builtin... " >&6; }
+if ${ac_cv_have___sync_bool_compare_and_swap+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+bool f(long* x, long y, long z) { return __sync_bool_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_bool_compare_and_swap=yes
+else
+  ac_cv_have___sync_bool_compare_and_swap=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_bool_compare_and_swap" >&5
+$as_echo "$ac_cv_have___sync_bool_compare_and_swap" >&6; }
+    if test $ac_cv_have___sync_bool_compare_and_swap = yes; then
+
+$as_echo "#define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_bool_compare_and_swap_8 builtin" >&5
+$as_echo_n "checking for __sync_bool_compare_and_swap_8 builtin... " >&6; }
+if ${ac_cv_have___sync_bool_compare_and_swap_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    bool f(int64_t* x, int64_t y, int64_t z) { return __sync_bool_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_bool_compare_and_swap_8=yes
+else
+  ac_cv_have___sync_bool_compare_and_swap_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_bool_compare_and_swap_8" >&5
+$as_echo "$ac_cv_have___sync_bool_compare_and_swap_8" >&6; }
+    if test $ac_cv_have___sync_bool_compare_and_swap_8 = yes; then
+
+$as_echo "#define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_val_compare_and_swap builtin" >&5
+$as_echo_n "checking for __sync_val_compare_and_swap builtin... " >&6; }
+if ${ac_cv_have___sync_val_compare_and_swap+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x, long y, long z) { return __sync_val_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_val_compare_and_swap=yes
+else
+  ac_cv_have___sync_val_compare_and_swap=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_val_compare_and_swap" >&5
+$as_echo "$ac_cv_have___sync_val_compare_and_swap" >&6; }
+    if test $ac_cv_have___sync_val_compare_and_swap = yes; then
+
+$as_echo "#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_val_compare_and_swap_8 builtin" >&5
+$as_echo_n "checking for __sync_val_compare_and_swap_8 builtin... " >&6; }
+if ${ac_cv_have___sync_val_compare_and_swap_8+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+    int64_t f(int64_t* x, int64_t y, int64_t z) { return __sync_val_compare_and_swap(x, y, z); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_val_compare_and_swap_8=yes
+else
+  ac_cv_have___sync_val_compare_and_swap_8=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_val_compare_and_swap_8" >&5
+$as_echo "$ac_cv_have___sync_val_compare_and_swap_8" >&6; }
+    if test $ac_cv_have___sync_val_compare_and_swap_8 = yes; then
+
+$as_echo "#define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_lock_test_and_set builtin" >&5
+$as_echo_n "checking for __sync_lock_test_and_set builtin... " >&6; }
+if ${ac_cv_have___sync_lock_test_and_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x) { return __sync_lock_test_and_set(x, 1); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_lock_test_and_set=yes
+else
+  ac_cv_have___sync_lock_test_and_set=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_lock_test_and_set" >&5
+$as_echo "$ac_cv_have___sync_lock_test_and_set" >&6; }
+    if test $ac_cv_have___sync_lock_test_and_set = yes; then
+
+$as_echo "#define HAVE___SYNC_LOCK_TEST_AND_SET 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_lock_test_and_set_val builtin" >&5
+$as_echo_n "checking for __sync_lock_test_and_set_val builtin... " >&6; }
+if ${ac_cv_have___sync_lock_test_and_set_val+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+long f(long* x, long y) { return __sync_lock_test_and_set(x, y); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_lock_test_and_set_val=yes
+else
+  ac_cv_have___sync_lock_test_and_set_val=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_lock_test_and_set_val" >&5
+$as_echo "$ac_cv_have___sync_lock_test_and_set_val" >&6; }
+    if test $ac_cv_have___sync_lock_test_and_set_val = yes; then
+
+$as_echo "#define HAVE___SYNC_LOCK_TEST_AND_SET_VAL 1" >>confdefs.h
+
+    fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_lock_release_set builtin" >&5
+$as_echo_n "checking for __sync_lock_release_set builtin... " >&6; }
+if ${ac_cv_have___sync_lock_release_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+void f(long* x) { __sync_lock_release(x); }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_have___sync_lock_release_set=yes
+else
+  ac_cv_have___sync_lock_release_set=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___sync_lock_release_set" >&5
+$as_echo "$ac_cv_have___sync_lock_release_set" >&6; }
+    if test $ac_cv_have___sync_lock_release_set = yes; then
+
+$as_echo "#define HAVE___SYNC_LOCK_RELEASE_SET 1" >>confdefs.h
+
+    fi
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands 'auto'" >&5
+$as_echo_n "checking whether the C++ compiler understands 'auto'... " >&6; }
+if ${ac_cv_cxx_auto+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_auto=yes
+else
+  ac_cv_cxx_auto=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_auto" >&5
+$as_echo "$ac_cv_cxx_auto" >&6; }
+if test "$ac_cv_cxx_auto" != yes -a -z "$ac_user_cxx"; then
+    CXX="${CXX} -std=gnu++0x"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler with -std=gnu++0x understands 'auto'" >&5
+$as_echo_n "checking whether the C++ compiler with -std=gnu++0x understands 'auto'... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+struct s { int a; }; int f(s x) { auto &y = x; return y.a; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_auto=yes
+else
+  ac_cv_cxx_auto=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_auto" >&5
+$as_echo "$ac_cv_cxx_auto" >&6; }
+fi
+
+if test "$ac_cv_cxx_auto" = yes; then
+
+$as_echo "#define HAVE_CXX_AUTO 1" >>confdefs.h
+
+else
+    as_fn_error $? "
+
+The C++ compiler does not appear to understand C++11.
+To fix this problem, try supplying a \"CXX\" argument to ./configure,
+such as \"./configure CXX='c++ -std=gnu++0x'\".
+
+========================================================" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands constexpr" >&5
+$as_echo_n "checking whether the C++ compiler understands constexpr... " >&6; }
+if ${ac_cv_cxx_constexpr+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+constexpr int f(int x) { return x + 1; }
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_constexpr=yes
+else
+  ac_cv_cxx_constexpr=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_constexpr" >&5
+$as_echo "$ac_cv_cxx_constexpr" >&6; }
+if test "$ac_cv_cxx_constexpr" = yes; then
+
+$as_echo "#define HAVE_CXX_CONSTEXPR 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands static_assert" >&5
+$as_echo_n "checking whether the C++ compiler understands static_assert... " >&6; }
+if ${ac_cv_cxx_static_assert+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+const int f = 2;
+int
+main ()
+{
+static_assert(f == 2, "f should be 2");
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_static_assert=yes
+else
+  ac_cv_cxx_static_assert=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_static_assert" >&5
+$as_echo "$ac_cv_cxx_static_assert" >&6; }
+if test "$ac_cv_cxx_static_assert" = yes; then
+
+$as_echo "#define HAVE_CXX_STATIC_ASSERT 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands rvalue references" >&5
+$as_echo_n "checking whether the C++ compiler understands rvalue references... " >&6; }
+if ${ac_cv_cxx_rvalue_references+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int f(int &) { return 1; } int f(int &&) { return 0; }
+int
+main ()
+{
+return f(int());
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_rvalue_references=yes
+else
+  ac_cv_cxx_rvalue_references=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_rvalue_references" >&5
+$as_echo "$ac_cv_cxx_rvalue_references" >&6; }
+if test "$ac_cv_cxx_rvalue_references" = yes; then
+
+$as_echo "#define HAVE_CXX_RVALUE_REFERENCES 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler understands template alias" >&5
+$as_echo_n "checking whether the C++ compiler understands template alias... " >&6; }
+if ${ac_cv_cxx_template_alias+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+template <typename T> struct X { typedef T type; }; template <typename T> using Y = X<T>; int f(int x) { return x; }
+int
+main ()
+{
+return f(Y<int>::type());
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_cxx_template_alias=yes
+else
+  ac_cv_cxx_template_alias=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_template_alias" >&5
+$as_echo "$ac_cv_cxx_template_alias" >&6; }
+if test "$ac_cv_cxx_template_alias" = yes; then
+
+$as_echo "#define HAVE_CXX_TEMPLATE_ALIAS 1" >>confdefs.h
+
+fi
+
+for ac_header in type_traits
+do :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "type_traits" "ac_cv_header_type_traits" "$ac_includes_default"
+if test "x$ac_cv_header_type_traits" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_TYPE_TRAITS 1
+_ACEOF
+
+fi
+
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::hash" >&5
+$as_echo_n "checking for std::hash... " >&6; }
+if ${ac_cv_have_std_hash+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <functional>
+#include <stddef.h>
+int
+main ()
+{
+std::hash<int> h; size_t x = h(1); return x == 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_std_hash=yes
+else
+  ac_cv_have_std_hash=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_std_hash" >&5
+$as_echo "$ac_cv_have_std_hash" >&6; }
+if test $ac_cv_have_std_hash = yes; then
+
+$as_echo "#define HAVE_STD_HASH 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __has_trivial_copy" >&5
+$as_echo_n "checking for __has_trivial_copy... " >&6; }
+if ${ac_cv_have___has_trivial_copy+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+long x = 1; if (__has_trivial_copy(long)) x = 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have___has_trivial_copy=yes
+else
+  ac_cv_have___has_trivial_copy=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___has_trivial_copy" >&5
+$as_echo "$ac_cv_have___has_trivial_copy" >&6; }
+if test $ac_cv_have___has_trivial_copy = yes; then
+
+$as_echo "#define HAVE___HAS_TRIVIAL_COPY 1" >>confdefs.h
+
+fi
+
+if test "$ac_cv_cxx_rvalue_references" = yes; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::move" >&5
+$as_echo_n "checking for std::move... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <utility>
+int
+main ()
+{
+long x = 0; long &&y = std::move(x);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_std_move=yes
+else
+  ac_cv_std_move=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_std_move" >&5
+$as_echo "$ac_cv_std_move" >&6; }
+    if test "$ac_cv_std_move" != yes; then
+        as_fn_error $? "
+
+The C++ compiler understands C++11, but does not have std::move.
+If you are using clang on Mac, ensure the -stdlib=libc++ option.
+
+========================================================" "$LINENO" 5
+    fi
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::is_trivially_copyable" >&5
+$as_echo_n "checking for std::is_trivially_copyable... " >&6; }
+if ${ac_cv_have_std_is_trivially_copyable+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <type_traits>
+int
+main ()
+{
+return std::is_trivially_copyable<int>::value;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_std_is_trivially_copyable=yes
+else
+  ac_cv_have_std_is_trivially_copyable=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_std_is_trivially_copyable" >&5
+$as_echo "$ac_cv_have_std_is_trivially_copyable" >&6; }
+if test $ac_cv_have_std_is_trivially_copyable = yes; then
+
+$as_echo "#define HAVE_STD_IS_TRIVIALLY_COPYABLE 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::is_rvalue_reference" >&5
+$as_echo_n "checking for std::is_rvalue_reference... " >&6; }
+if ${ac_cv_have_std_is_rvalue_reference+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <type_traits>
+int
+main ()
+{
+return std::is_rvalue_reference<int>::value;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_std_is_rvalue_reference=yes
+else
+  ac_cv_have_std_is_rvalue_reference=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_std_is_rvalue_reference" >&5
+$as_echo "$ac_cv_have_std_is_rvalue_reference" >&6; }
+if test $ac_cv_have_std_is_rvalue_reference = yes; then
+
+$as_echo "#define HAVE_STD_IS_RVALUE_REFERENCE 1" >>confdefs.h
+
+fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc in -lflow" >&5
+$as_echo_n "checking for malloc in -lflow... " >&6; }
+if ${ac_cv_lib_flow_malloc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lflow  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char malloc ();
+int
+main ()
+{
+return malloc ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_flow_malloc=yes
+else
+  ac_cv_lib_flow_malloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_flow_malloc" >&5
+$as_echo "$ac_cv_lib_flow_malloc" >&6; }
+if test "x$ac_cv_lib_flow_malloc" = xyes; then :
+  have_flow=true
+else
+  have_flow=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mallctl in -ljemalloc" >&5
+$as_echo_n "checking for mallctl in -ljemalloc... " >&6; }
+if ${ac_cv_lib_jemalloc_mallctl+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ljemalloc  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char mallctl ();
+int
+main ()
+{
+return mallctl ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_jemalloc_mallctl=yes
+else
+  ac_cv_lib_jemalloc_mallctl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jemalloc_mallctl" >&5
+$as_echo "$ac_cv_lib_jemalloc_mallctl" >&6; }
+if test "x$ac_cv_lib_jemalloc_mallctl" = xyes; then :
+  have_jemalloc=true
+else
+  have_jemalloc=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tc_malloc in -ltcmalloc_minimal" >&5
+$as_echo_n "checking for tc_malloc in -ltcmalloc_minimal... " >&6; }
+if ${ac_cv_lib_tcmalloc_minimal_tc_malloc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltcmalloc_minimal  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tc_malloc ();
+int
+main ()
+{
+return tc_malloc ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_tcmalloc_minimal_tc_malloc=yes
+else
+  ac_cv_lib_tcmalloc_minimal_tc_malloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tcmalloc_minimal_tc_malloc" >&5
+$as_echo "$ac_cv_lib_tcmalloc_minimal_tc_malloc" >&6; }
+if test "x$ac_cv_lib_tcmalloc_minimal_tc_malloc" = xyes; then :
+  have_tcmalloc_minimal=true
+else
+  have_tcmalloc_minimal=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Z16getMainHoardHeapv in -lhoard" >&5
+$as_echo_n "checking for _Z16getMainHoardHeapv in -lhoard... " >&6; }
+if ${ac_cv_lib_hoard__Z16getMainHoardHeapv+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lhoard  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char _Z16getMainHoardHeapv ();
+int
+main ()
+{
+return _Z16getMainHoardHeapv ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_hoard__Z16getMainHoardHeapv=yes
+else
+  ac_cv_lib_hoard__Z16getMainHoardHeapv=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_hoard__Z16getMainHoardHeapv" >&5
+$as_echo "$ac_cv_lib_hoard__Z16getMainHoardHeapv" >&6; }
+if test "x$ac_cv_lib_hoard__Z16getMainHoardHeapv" = xyes; then :
+  have_hoard=true
+else
+  have_hoard=
+fi
+
+
+
+# Check whether --with-malloc was given.
+if test "${with_malloc+set}" = set; then :
+  withval=$with_malloc; ac_mtd_malloc=$withval
+else
+  ac_mtd_malloc=yes
+fi
+
+
+if test \( "$ac_mtd_malloc" = tcmalloc -a -z "$have_tcmalloc_minimal" \) \
+       -o \( "$ac_mtd_malloc" = jemalloc -a -z "$have_jemalloc" \) \
+       -o \( "$ac_mtd_malloc" = flow -a -z "$have_flow" \) \
+        -o \( "$ac_mtd_malloc" = hoard -a -z "$have_hoard" \) ; then
+    as_fn_error $? "$ac_mtd_malloc not found" "$LINENO" 5
+elif test "$ac_mtd_malloc" = tcmalloc -o "$ac_mtd_malloc" = jemalloc -o "$ac_mtd_malloc" = flow -o "$ac_mtd_malloc" = hoard; then
+    :
+elif test "$ac_mtd_malloc" = yes -o "$ac_mtd_malloc" = default; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc library" >&5
+$as_echo_n "checking for malloc library... " >&6; }
+    if test -n "$have_flow"; then ac_mtd_malloc=flow;
+    elif test -n "$have_jemalloc"; then ac_mtd_malloc=jemalloc;
+    elif test -n "$have_tcmalloc_minimal"; then ac_mtd_malloc=tcmalloc;
+    else ac_mtd_malloc=malloc; fi
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_mtd_malloc" >&5
+$as_echo "$ac_mtd_malloc" >&6; }
+elif test "$ac_mtd_malloc" = no -o "$ac_mtd_malloc" = malloc -o -z "$ac_mtd_malloc"; then
+    ac_mtd_malloc=malloc
+else
+    as_fn_error $? "Unknown malloc type $ac_mtd_malloc" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc library" >&5
+$as_echo_n "checking for malloc library... " >&6; }
+if test "$ac_mtd_malloc" = tcmalloc; then
+    MALLOC_LIBS="-ltcmalloc_minimal"
+
+$as_echo "#define HAVE_TCMALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -ltcmalloc_minimal" >&5
+$as_echo "-ltcmalloc_minimal" >&6; }
+elif test "$ac_mtd_malloc" = jemalloc; then
+    MALLOC_LIBS="-ljemalloc"
+
+$as_echo "#define HAVE_JEMALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -ljemalloc" >&5
+$as_echo "-ljemalloc" >&6; }
+elif test "$ac_mtd_malloc" = flow; then
+    MALLOC_LIBS="-lflow"
+
+$as_echo "#define HAVE_FLOW_MALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -lflow" >&5
+$as_echo "-lflow" >&6; }
+elif test "$ac_mtd_malloc" = hoard; then
+    MALLOC_LIBS="-lhoard"
+
+$as_echo "#define HAVE_HOARD_MALLOC 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: -lhoard" >&5
+$as_echo "-lhoard" >&6; }
+else
+    MALLOC_LIBS=
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: default" >&5
+$as_echo "default" >&6; }
+fi
+
+
+
+
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether off_t and long are the same type" >&5
+$as_echo_n "checking whether off_t and long are the same type... " >&6; }
+if ${ac_cv_have_same_type_off_t_is_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(off_t) {return 0;} int f(long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_off_t_is_long=no
+else
+  ac_cv_have_same_type_off_t_is_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_off_t_is_long" >&5
+$as_echo "$ac_cv_have_same_type_off_t_is_long" >&6; }
+    if test $ac_cv_have_same_type_off_t_is_long = yes; then
+
+$as_echo "#define HAVE_OFF_T_IS_LONG 1" >>confdefs.h
+
+    fi
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether off_t and long long are the same type" >&5
+$as_echo_n "checking whether off_t and long long are the same type... " >&6; }
+if ${ac_cv_have_same_type_off_t_is_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(off_t) {return 0;} int f(long long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_off_t_is_long_long=no
+else
+  ac_cv_have_same_type_off_t_is_long_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_off_t_is_long_long" >&5
+$as_echo "$ac_cv_have_same_type_off_t_is_long_long" >&6; }
+    if test $ac_cv_have_same_type_off_t_is_long_long = yes; then
+
+$as_echo "#define HAVE_OFF_T_IS_LONG_LONG 1" >>confdefs.h
+
+    fi
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether int64_t and long are the same type" >&5
+$as_echo_n "checking whether int64_t and long are the same type... " >&6; }
+if ${ac_cv_have_same_type_int64_t_is_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+int f(int64_t) {return 0;} int f(long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_int64_t_is_long=no
+else
+  ac_cv_have_same_type_int64_t_is_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_int64_t_is_long" >&5
+$as_echo "$ac_cv_have_same_type_int64_t_is_long" >&6; }
+    if test $ac_cv_have_same_type_int64_t_is_long = yes; then
+
+$as_echo "#define HAVE_INT64_T_IS_LONG 1" >>confdefs.h
+
+    fi
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether int64_t and long long are the same type" >&5
+$as_echo_n "checking whether int64_t and long long are the same type... " >&6; }
+if ${ac_cv_have_same_type_int64_t_is_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+int f(int64_t) {return 0;} int f(long long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_int64_t_is_long_long=no
+else
+  ac_cv_have_same_type_int64_t_is_long_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_int64_t_is_long_long" >&5
+$as_echo "$ac_cv_have_same_type_int64_t_is_long_long" >&6; }
+    if test $ac_cv_have_same_type_int64_t_is_long_long = yes; then
+
+$as_echo "#define HAVE_INT64_T_IS_LONG_LONG 1" >>confdefs.h
+
+    fi
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t and unsigned are the same type" >&5
+$as_echo_n "checking whether size_t and unsigned are the same type... " >&6; }
+if ${ac_cv_have_same_type_size_t_is_unsigned+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(size_t) {return 0;} int f(unsigned) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_size_t_is_unsigned=no
+else
+  ac_cv_have_same_type_size_t_is_unsigned=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_size_t_is_unsigned" >&5
+$as_echo "$ac_cv_have_same_type_size_t_is_unsigned" >&6; }
+    if test $ac_cv_have_same_type_size_t_is_unsigned = yes; then
+
+$as_echo "#define HAVE_SIZE_T_IS_UNSIGNED 1" >>confdefs.h
+
+    fi
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t and unsigned long are the same type" >&5
+$as_echo_n "checking whether size_t and unsigned long are the same type... " >&6; }
+if ${ac_cv_have_same_type_size_t_is_unsigned_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(size_t) {return 0;} int f(unsigned long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_size_t_is_unsigned_long=no
+else
+  ac_cv_have_same_type_size_t_is_unsigned_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_size_t_is_unsigned_long" >&5
+$as_echo "$ac_cv_have_same_type_size_t_is_unsigned_long" >&6; }
+    if test $ac_cv_have_same_type_size_t_is_unsigned_long = yes; then
+
+$as_echo "#define HAVE_SIZE_T_IS_UNSIGNED_LONG 1" >>confdefs.h
+
+    fi
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether size_t and unsigned long long are the same type" >&5
+$as_echo_n "checking whether size_t and unsigned long long are the same type... " >&6; }
+if ${ac_cv_have_same_type_size_t_is_unsigned_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int f(size_t) {return 0;} int f(unsigned long long) {return 0;}
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_have_same_type_size_t_is_unsigned_long_long=no
+else
+  ac_cv_have_same_type_size_t_is_unsigned_long_long=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_same_type_size_t_is_unsigned_long_long" >&5
+$as_echo "$ac_cv_have_same_type_size_t_is_unsigned_long_long" >&6; }
+    if test $ac_cv_have_same_type_size_t_is_unsigned_long_long = yes; then
+
+$as_echo "#define HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG 1" >>confdefs.h
+
+    fi
+
+
+
+ac_fn_cxx_check_type "$LINENO" "long long" "ac_cv_type_long_long" "$ac_includes_default"
+if test "x$ac_cv_type_long_long" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LONG_LONG 1
+_ACEOF
+
+
+fi
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of short" >&5
+$as_echo_n "checking size of short... " >&6; }
+if ${ac_cv_sizeof_short+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_short" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (short)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_short=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5
+$as_echo "$ac_cv_sizeof_short" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_SHORT $ac_cv_sizeof_short
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5
+$as_echo_n "checking size of int... " >&6; }
+if ${ac_cv_sizeof_int+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_int" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (int)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_int=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5
+$as_echo "$ac_cv_sizeof_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5
+$as_echo_n "checking size of long... " >&6; }
+if ${ac_cv_sizeof_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5
+$as_echo "$ac_cv_sizeof_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5
+$as_echo_n "checking size of long long... " >&6; }
+if ${ac_cv_sizeof_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_long_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long long)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_long_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5
+$as_echo "$ac_cv_sizeof_long_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5
+$as_echo_n "checking size of void *... " >&6; }
+if ${ac_cv_sizeof_void_p+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_void_p" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (void *)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_void_p=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5
+$as_echo "$ac_cv_sizeof_void_p" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_VOID_P $ac_cv_sizeof_void_p
+_ACEOF
+
+
+
+ac_fn_cxx_check_decl "$LINENO" "getline" "ac_cv_have_decl_getline" "$ac_includes_default"
+if test "x$ac_cv_have_decl_getline" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_GETLINE $ac_have_decl
+_ACEOF
+
+
+for ac_header in time.h execinfo.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+ac_fn_cxx_check_decl "$LINENO" "clock_gettime" "ac_cv_have_decl_clock_gettime" "#if HAVE_TIME_H
+# include <time.h>
+#endif
+"
+if test "x$ac_cv_have_decl_clock_gettime" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_CLOCK_GETTIME $ac_have_decl
+_ACEOF
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
+$as_echo_n "checking for library containing clock_gettime... " >&6; }
+if ${ac_cv_search_clock_gettime+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' rt; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_search_clock_gettime=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_clock_gettime+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_clock_gettime+:} false; then :
+
+else
+  ac_cv_search_clock_gettime=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
+$as_echo "$ac_cv_search_clock_gettime" >&6; }
+ac_res=$ac_cv_search_clock_gettime
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+for ac_func in clock_gettime
+do :
+  ac_fn_cxx_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime"
+if test "x$ac_cv_func_clock_gettime" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_CLOCK_GETTIME 1
+_ACEOF
+
+fi
+done
+
+
+
+# Check whether --enable-row-type was given.
+if test "${enable_row_type+set}" = set; then :
+  enableval=$enable_row_type; ac_cv_row_type=$enableval
+else
+  ac_cv_row_type=bag
+fi
+
+if test "$ac_cv_row_type" = array; then
+
+cat >>confdefs.h <<_ACEOF
+#define MASSTREE_ROW_TYPE_ARRAY 1
+_ACEOF
+
+elif test "$ac_cv_row_type" = array_ver; then
+
+cat >>confdefs.h <<_ACEOF
+#define MASSTREE_ROW_TYPE_ARRAY_VER 1
+_ACEOF
+
+elif test "$ac_cv_row_type" = bag; then
+
+cat >>confdefs.h <<_ACEOF
+#define MASSTREE_ROW_TYPE_BAG 1
+_ACEOF
+
+elif test "$ac_cv_row_type" = str; then
+
+cat >>confdefs.h <<_ACEOF
+#define MASSTREE_ROW_TYPE_STR 1
+_ACEOF
+
+else
+    as_fn_error $? "$ac_cv_row_type: Unknown row type" "$LINENO" 5
+fi
+
+# Check whether --enable-max-key-len was given.
+if test "${enable_max_key_len+set}" = set; then :
+  enableval=$enable_max_key_len; ac_cv_max_key_len=$enableval
+else
+  ac_cv_max_key_len=255
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define MASSTREE_MAXKEYLEN $ac_cv_max_key_len
+_ACEOF
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether MADV_HUGEPAGE is supported" >&5
+$as_echo_n "checking whether MADV_HUGEPAGE is supported... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/mman.h>
+#ifndef MADV_HUGEPAGE
+#error "no"
+#endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  have_madv_hugepage=yes
+else
+  have_madv_hugepage=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_madv_hugepage" >&5
+$as_echo "$have_madv_hugepage" >&6; }
+if test $have_madv_hugepage = yes; then
+
+$as_echo "#define HAVE_MADV_HUGEPAGE 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether MAP_HUGETLB is supported" >&5
+$as_echo_n "checking whether MAP_HUGETLB is supported... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/mman.h>
+#ifndef MAP_HUGETLB
+#error "no"
+#endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  have_map_hugetlb=yes
+else
+  have_map_hugetlb=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_map_hugetlb" >&5
+$as_echo "$have_map_hugetlb" >&6; }
+if test $have_map_hugetlb = yes; then
+
+$as_echo "#define HAVE_MAP_HUGETLB 1" >>confdefs.h
+
+fi
+
+# Check whether --enable-superpage was given.
+if test "${enable_superpage+set}" = set; then :
+  enableval=$enable_superpage;
+else
+  enable_superpage=maybe
+fi
+
+if test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" = "yes no no"; then
+    as_fn_error $? "
+Error: superpages are not supported on this machine.
+Try again without --enable-superpage.
+" "$LINENO" 5
+elif test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" != "maybe no no" -a "$enable_superpage" != no; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SUPERPAGE 1
+_ACEOF
+
+fi
+
+# Check whether --enable-memdebug was given.
+if test "${enable_memdebug+set}" = set; then :
+  enableval=$enable_memdebug;
+fi
+
+if test "$enable_memdebug" = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_MEMDEBUG 1
+_ACEOF
+
+fi
+
+# Check whether --enable-assert was given.
+if test "${enable_assert+set}" = set; then :
+  enableval=$enable_assert; { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Use --disable-assertions instead of --disable-assert." >&5
+$as_echo "$as_me: WARNING: Use --disable-assertions instead of --disable-assert." >&2;}
+fi
+
+# Check whether --enable-assertions was given.
+if test "${enable_assertions+set}" = set; then :
+  enableval=$enable_assertions;
+fi
+
+if test "$enable_assertions" != no -o "(" -z "$enable_assertions" -a "$enable_assert" != no ")"; then
+
+cat >>confdefs.h <<_ACEOF
+#define ENABLE_ASSERTIONS 1
+_ACEOF
+
+fi
+
+# Check whether --enable-preconditions was given.
+if test "${enable_preconditions+set}" = set; then :
+  enableval=$enable_preconditions;
+fi
+
+if test "$enable_preconditions" = no; then
+
+cat >>confdefs.h <<_ACEOF
+#define ENABLE_PRECONDITIONS 0
+_ACEOF
+
+elif test -n "$enable_preconditions"; then
+
+cat >>confdefs.h <<_ACEOF
+#define ENABLE_PRECONDITIONS 1
+_ACEOF
+
+fi
+
+# Check whether --enable-invariants was given.
+if test "${enable_invariants+set}" = set; then :
+  enableval=$enable_invariants;
+fi
+
+if test "$enable_invariants" = no; then
+
+cat >>confdefs.h <<_ACEOF
+#define ENABLE_INVARIANTS 0
+_ACEOF
+
+elif test -n "$enable_preconditions"; then
+
+cat >>confdefs.h <<_ACEOF
+#define ENABLE_INVARIANTS 1
+_ACEOF
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define CACHE_LINE_SIZE 64
+_ACEOF
+
+
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UNALIGNED_ACCESS 1
+_ACEOF
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+       cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+         mv -f confcache "$cache_file"$$ &&
+         mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+         mv -f confcache "$cache_file" ;;
+       esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by masstree-beta $as_me 0.1, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+masstree-beta config.status 0.1
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "GNUmakefile") CONFIG_FILES="$CONFIG_FILES GNUmakefile" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=[    ]*/{
+h
+s///
+s/^/:/
+s/[     ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[  ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/silo/masstree/configure.ac b/silo/masstree/configure.ac
new file mode 100644 (file)
index 0000000..af27d29
--- /dev/null
@@ -0,0 +1,438 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT([masstree-beta], [0.1])
+AC_PREREQ(2.60)
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([GNUmakefile])
+AC_SUBST(ac_configure_args)
+
+ac_user_cxx=${CXX+y}
+
+AC_PROG_CC
+AC_PROG_CXX
+AC_LANG_CPLUSPLUS
+
+AC_DEFINE([WORDS_BIGENDIAN_SET], [1], [Define if WORDS_BIGENDIAN has been set.])
+AC_C_BIGENDIAN()
+
+AC_CHECK_HEADERS([sys/epoll.h numa.h])
+
+AC_SEARCH_LIBS([numa_available], [numa], [AC_DEFINE([HAVE_LIBNUMA], [1], [Define if you have libnuma.])])
+
+
+dnl Builtins
+
+AC_DEFUN([KVDB_CHECK_BUILTIN], [
+    AC_CACHE_CHECK([for $1 builtin], [ac_cv_have_$1],
+       [AC_LINK_IFELSE([AC_LANG_PROGRAM([$2], [])],
+           [ac_cv_have_$1=yes], [ac_cv_have_$1=no])])
+    if test $ac_cv_have_$1 = yes; then
+       AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1], [Define if you have the $1 builtin.])
+    fi
+])
+
+KVDB_CHECK_BUILTIN([__builtin_clz],
+    [[unsigned f(unsigned x) { return __builtin_clz(x); }]])
+
+KVDB_CHECK_BUILTIN([__builtin_clzl],
+    [[unsigned long f(unsigned long x) { return __builtin_clzl(x); }]])
+
+KVDB_CHECK_BUILTIN([__builtin_clzll],
+    [[unsigned long long f(unsigned long long x) { return __builtin_clzll(x); }]])
+
+KVDB_CHECK_BUILTIN([__builtin_ctz],
+    [[unsigned f(unsigned x) { return __builtin_ctz(x); }]])
+
+KVDB_CHECK_BUILTIN([__builtin_ctzl],
+    [[unsigned long f(unsigned long x) { return __builtin_ctzl(x); }]])
+
+KVDB_CHECK_BUILTIN([__builtin_ctzll],
+    [[unsigned long long f(unsigned long long x) { return __builtin_ctzll(x); }]])
+
+KVDB_CHECK_BUILTIN([__sync_synchronize], [[long x = 11;
+    void f(long i) { long* y = &x; __sync_synchronize(); *y = i; }]])
+
+KVDB_CHECK_BUILTIN([__sync_fetch_and_add],
+    [[long f(long* x) { return __sync_fetch_and_add(x, 2L); }]])
+
+KVDB_CHECK_BUILTIN([__sync_add_and_fetch],
+    [[long f(long* x) { return __sync_add_and_fetch(x, 2L); }]])
+
+KVDB_CHECK_BUILTIN([__sync_fetch_and_add_8],
+    [[#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_add(x, (int64_t) 2); }]])
+
+KVDB_CHECK_BUILTIN([__sync_add_and_fetch_8],
+    [[#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_add_and_fetch(x, (int64_t) 2); }]])
+
+KVDB_CHECK_BUILTIN([__sync_fetch_and_or],
+    [[long f(long* x) { return __sync_fetch_and_or(x, 2L); }]])
+
+KVDB_CHECK_BUILTIN([__sync_or_and_fetch],
+    [[long f(long* x) { return __sync_or_and_fetch(x, 2L); }]])
+
+KVDB_CHECK_BUILTIN([__sync_fetch_and_or_8],
+    [[#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_fetch_and_or(x, (int64_t) 2); }]])
+
+KVDB_CHECK_BUILTIN([__sync_or_and_fetch_8],
+    [[#include <stdint.h>
+    int64_t f(int64_t* x) { return __sync_or_and_fetch(x, (int64_t) 2); }]])
+
+KVDB_CHECK_BUILTIN([__sync_bool_compare_and_swap],
+    [[bool f(long* x, long y, long z) { return __sync_bool_compare_and_swap(x, y, z); }]])
+
+KVDB_CHECK_BUILTIN([__sync_bool_compare_and_swap_8],
+    [[#include <stdint.h>
+    bool f(int64_t* x, int64_t y, int64_t z) { return __sync_bool_compare_and_swap(x, y, z); }]])
+
+KVDB_CHECK_BUILTIN([__sync_val_compare_and_swap],
+    [[long f(long* x, long y, long z) { return __sync_val_compare_and_swap(x, y, z); }]])
+
+KVDB_CHECK_BUILTIN([__sync_val_compare_and_swap_8],
+    [[#include <stdint.h>
+    int64_t f(int64_t* x, int64_t y, int64_t z) { return __sync_val_compare_and_swap(x, y, z); }]])
+
+KVDB_CHECK_BUILTIN([__sync_lock_test_and_set],
+    [[long f(long* x) { return __sync_lock_test_and_set(x, 1); }]])
+
+KVDB_CHECK_BUILTIN([__sync_lock_test_and_set_val],
+    [[long f(long* x, long y) { return __sync_lock_test_and_set(x, y); }]])
+
+KVDB_CHECK_BUILTIN([__sync_lock_release_set],
+    [[void f(long* x) { __sync_lock_release(x); }]])
+
+
+dnl C++ features
+
+AC_CACHE_CHECK([whether the C++ compiler understands 'auto'], [ac_cv_cxx_auto], [
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[struct s { int a; }; int f(s x) { auto &y = x; return y.a; }]], [[]])],
+       [ac_cv_cxx_auto=yes], [ac_cv_cxx_auto=no])])
+if test "$ac_cv_cxx_auto" != yes -a -z "$ac_user_cxx"; then
+    CXX="${CXX} -std=gnu++0x"
+    AC_MSG_CHECKING([whether the C++ compiler with -std=gnu++0x understands 'auto'])
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[struct s { int a; }; int f(s x) { auto &y = x; return y.a; }]], [[]])],
+       [ac_cv_cxx_auto=yes], [ac_cv_cxx_auto=no])
+    AC_MSG_RESULT([$ac_cv_cxx_auto])
+fi
+
+if test "$ac_cv_cxx_auto" = yes; then
+    AC_DEFINE([HAVE_CXX_AUTO], [1], [Define if the C++ compiler understands 'auto'.])
+else
+    AC_MSG_ERROR([
+
+The C++ compiler does not appear to understand C++11.
+To fix this problem, try supplying a "CXX" argument to ./configure,
+such as "./configure CXX='c++ -std=gnu++0x'".
+
+========================================================])
+fi
+
+AC_CACHE_CHECK([whether the C++ compiler understands constexpr], [ac_cv_cxx_constexpr], [
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[constexpr int f(int x) { return x + 1; }]], [[]])],
+       [ac_cv_cxx_constexpr=yes], [ac_cv_cxx_constexpr=no])])
+if test "$ac_cv_cxx_constexpr" = yes; then
+    AC_DEFINE([HAVE_CXX_CONSTEXPR], [1], [Define if the C++ compiler understands constexpr.])
+fi
+
+AC_CACHE_CHECK([whether the C++ compiler understands static_assert], [ac_cv_cxx_static_assert], [
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[const int f = 2;]], [[static_assert(f == 2, "f should be 2");]])],
+       [ac_cv_cxx_static_assert=yes], [ac_cv_cxx_static_assert=no])])
+if test "$ac_cv_cxx_static_assert" = yes; then
+    AC_DEFINE([HAVE_CXX_STATIC_ASSERT], [1], [Define if the C++ compiler understands static_assert.])
+fi
+
+AC_CACHE_CHECK([whether the C++ compiler understands rvalue references], [ac_cv_cxx_rvalue_references], [
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int f(int &) { return 1; } int f(int &&) { return 0; }]], [[return f(int());]])],
+       [ac_cv_cxx_rvalue_references=yes], [ac_cv_cxx_rvalue_references=no])])
+if test "$ac_cv_cxx_rvalue_references" = yes; then
+    AC_DEFINE([HAVE_CXX_RVALUE_REFERENCES], [1], [Define if the C++ compiler understands rvalue references.])
+fi
+
+AC_CACHE_CHECK([whether the C++ compiler understands template alias], [ac_cv_cxx_template_alias], [
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[template <typename T> struct X { typedef T type; }; template <typename T> using Y = X<T>; int f(int x) { return x; }]], [[return f(Y<int>::type());]])],
+       [ac_cv_cxx_template_alias=yes], [ac_cv_cxx_template_alias=no])])
+if test "$ac_cv_cxx_template_alias" = yes; then
+    AC_DEFINE([HAVE_CXX_TEMPLATE_ALIAS], [1], [Define if the C++ compiler understands template alias.])
+fi
+
+AC_CHECK_HEADERS([type_traits])
+
+AC_CACHE_CHECK([for std::hash], [ac_cv_have_std_hash], [
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <functional>
+#include <stddef.h>],
+        [[std::hash<int> h; size_t x = h(1); return x == 0;]])],
+        [ac_cv_have_std_hash=yes], [ac_cv_have_std_hash=no])])
+if test $ac_cv_have_std_hash = yes; then
+    AC_DEFINE([HAVE_STD_HASH], [1], [Define if you have std::hash.])
+fi
+
+AC_CACHE_CHECK([for __has_trivial_copy], [ac_cv_have___has_trivial_copy], [
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[long x = 1; if (__has_trivial_copy(long)) x = 0;]])], [ac_cv_have___has_trivial_copy=yes], [ac_cv_have___has_trivial_copy=no])])
+if test $ac_cv_have___has_trivial_copy = yes; then
+    AC_DEFINE([HAVE___HAS_TRIVIAL_COPY], [1], [Define if you have the __has_trivial_copy compiler intrinsic.])
+fi
+
+if test "$ac_cv_cxx_rvalue_references" = yes; then
+    AC_MSG_CHECKING([for std::move])
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <utility>], [[long x = 0; long &&y = std::move(x);]])], [ac_cv_std_move=yes], [ac_cv_std_move=no])
+    AC_MSG_RESULT([$ac_cv_std_move])
+    if test "$ac_cv_std_move" != yes; then
+        AC_MSG_ERROR([
+
+The C++ compiler understands C++11, but does not have std::move.
+If you are using clang on Mac, ensure the -stdlib=libc++ option.
+
+========================================================])
+    fi
+fi
+
+AC_CACHE_CHECK([for std::is_trivially_copyable], [ac_cv_have_std_is_trivially_copyable], [
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <type_traits>], [[return std::is_trivially_copyable<int>::value;]])], [ac_cv_have_std_is_trivially_copyable=yes], [ac_cv_have_std_is_trivially_copyable=no])])
+if test $ac_cv_have_std_is_trivially_copyable = yes; then
+    AC_DEFINE([HAVE_STD_IS_TRIVIALLY_COPYABLE], [1], [Define if you have the std::is_trivially_copyable template.])
+fi
+
+AC_CACHE_CHECK([for std::is_rvalue_reference], [ac_cv_have_std_is_rvalue_reference], [
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <type_traits>], [[return std::is_rvalue_reference<int>::value;]])], [ac_cv_have_std_is_rvalue_reference=yes], [ac_cv_have_std_is_rvalue_reference=no])])
+if test $ac_cv_have_std_is_rvalue_reference = yes; then
+    AC_DEFINE([HAVE_STD_IS_RVALUE_REFERENCE], [1], [Define if you have the std::is_rvalue_reference template.])
+fi
+
+
+dnl Memory allocator
+
+AC_CHECK_LIB([flow], [malloc], [have_flow=true], [have_flow=])
+AC_CHECK_LIB([jemalloc], [mallctl], [have_jemalloc=true], [have_jemalloc=])
+AC_CHECK_LIB([tcmalloc_minimal], [tc_malloc], [have_tcmalloc_minimal=true], [have_tcmalloc_minimal=])
+AC_CHECK_LIB([hoard], [_Z16getMainHoardHeapv], [have_hoard=true], [have_hoard=])
+
+AC_ARG_WITH([malloc],
+    [AS_HELP_STRING([--with-malloc=TYPE],
+                    [memory allocator (malloc|jemalloc|tcmalloc|hoard|flow)])],
+    [ac_mtd_malloc=$withval], [ac_mtd_malloc=yes])
+
+if test \( "$ac_mtd_malloc" = tcmalloc -a -z "$have_tcmalloc_minimal" \) \
+       -o \( "$ac_mtd_malloc" = jemalloc -a -z "$have_jemalloc" \) \
+       -o \( "$ac_mtd_malloc" = flow -a -z "$have_flow" \) \
+        -o \( "$ac_mtd_malloc" = hoard -a -z "$have_hoard" \) ; then
+    AC_MSG_ERROR([$ac_mtd_malloc not found])
+elif test "$ac_mtd_malloc" = tcmalloc -o "$ac_mtd_malloc" = jemalloc -o "$ac_mtd_malloc" = flow -o "$ac_mtd_malloc" = hoard; then
+    :
+elif test "$ac_mtd_malloc" = yes -o "$ac_mtd_malloc" = default; then
+    AC_MSG_CHECKING([for malloc library])
+    if test -n "$have_flow"; then ac_mtd_malloc=flow;
+    elif test -n "$have_jemalloc"; then ac_mtd_malloc=jemalloc;
+    elif test -n "$have_tcmalloc_minimal"; then ac_mtd_malloc=tcmalloc;
+    else ac_mtd_malloc=malloc; fi
+    AC_MSG_RESULT([$ac_mtd_malloc])
+elif test "$ac_mtd_malloc" = no -o "$ac_mtd_malloc" = malloc -o -z "$ac_mtd_malloc"; then
+    ac_mtd_malloc=malloc
+else
+    AC_MSG_ERROR([Unknown malloc type $ac_mtd_malloc])
+fi
+
+AC_MSG_CHECKING([for malloc library])
+if test "$ac_mtd_malloc" = tcmalloc; then
+    MALLOC_LIBS="-ltcmalloc_minimal"
+    AC_DEFINE([HAVE_TCMALLOC], [1], [Define if you are using libtcmalloc for malloc.])
+    AC_MSG_RESULT([-ltcmalloc_minimal])
+elif test "$ac_mtd_malloc" = jemalloc; then
+    MALLOC_LIBS="-ljemalloc"
+    AC_DEFINE([HAVE_JEMALLOC], [1], [Define if you are using libjemalloc for malloc.])
+    AC_MSG_RESULT([-ljemalloc])
+elif test "$ac_mtd_malloc" = flow; then
+    MALLOC_LIBS="-lflow"
+    AC_DEFINE([HAVE_FLOW_MALLOC], [1], [Define if you are using libflow for malloc.])
+    AC_MSG_RESULT([-lflow])
+elif test "$ac_mtd_malloc" = hoard; then
+    MALLOC_LIBS="-lhoard"
+    AC_DEFINE([HAVE_HOARD_MALLOC], [1], [Define if you are using libhoard for malloc.])
+    AC_MSG_RESULT([-lhoard])
+else
+    MALLOC_LIBS=
+    AC_MSG_RESULT([default])
+fi
+AC_SUBST(MALLOC_LIBS)
+
+
+dnl Types
+
+AC_DEFUN([KVDB_CHECK_SAME_TYPE], [
+    pushdef([KVDB_CST_VAR], [AS_TR_SH([ac_cv_have_same_type_$1_is_$2])])
+    AC_CACHE_CHECK([whether $1 and $2 are the same type], KVDB_CST_VAR,
+       [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([$3
+int f($1) {return 0;} int f($2) {return 0;}], [])],
+           [KVDB_CST_VAR=no], [KVDB_CST_VAR=yes])])
+    if test $KVDB_CST_VAR = yes; then
+       AC_DEFINE(AS_TR_CPP([HAVE_$1_IS_$2]), [1], [Define if $1 and $2 are the same type.])
+    fi
+    popdef([KVDB_CST_VAR])
+])
+
+KVDB_CHECK_SAME_TYPE([off_t], [long], [#include <stdio.h>])
+KVDB_CHECK_SAME_TYPE([off_t], [long long], [#include <stdio.h>])
+KVDB_CHECK_SAME_TYPE([int64_t], [long], [#include <stdint.h>])
+KVDB_CHECK_SAME_TYPE([int64_t], [long long], [#include <stdint.h>])
+KVDB_CHECK_SAME_TYPE([size_t], [unsigned], [#include <stdio.h>])
+KVDB_CHECK_SAME_TYPE([size_t], [unsigned long], [#include <stdio.h>])
+KVDB_CHECK_SAME_TYPE([size_t], [unsigned long long], [#include <stdio.h>])
+
+AC_CHECK_TYPES([long long])
+AC_CHECK_SIZEOF([short])
+AC_CHECK_SIZEOF([int])
+AC_CHECK_SIZEOF([long])
+AC_CHECK_SIZEOF([long long])
+AC_CHECK_SIZEOF([void *])
+
+AC_CHECK_DECLS([getline])
+
+AC_CHECK_HEADERS([time.h execinfo.h])
+AC_CHECK_DECLS([clock_gettime], [], [], [#if HAVE_TIME_H
+# include <time.h>
+#endif])
+AC_SEARCH_LIBS([clock_gettime], [rt])
+AC_CHECK_FUNCS([clock_gettime])
+
+
+AC_ARG_ENABLE([row-type],
+    [AS_HELP_STRING([--enable-row-type=ARG],
+                    [row type: bag array array_ver str, default bag])],
+    [ac_cv_row_type=$enableval], [ac_cv_row_type=bag])
+if test "$ac_cv_row_type" = array; then
+    AC_DEFINE_UNQUOTED([MASSTREE_ROW_TYPE_ARRAY], [1], [Define if the default row type is value_timed_array.])
+elif test "$ac_cv_row_type" = array_ver; then
+    AC_DEFINE_UNQUOTED([MASSTREE_ROW_TYPE_ARRAY_VER], [1], [Define if the default row type is value_timed_array_ver.])
+elif test "$ac_cv_row_type" = bag; then
+    AC_DEFINE_UNQUOTED([MASSTREE_ROW_TYPE_BAG], [1], [Define if the default row type is value_timed_bag.])
+elif test "$ac_cv_row_type" = str; then
+    AC_DEFINE_UNQUOTED([MASSTREE_ROW_TYPE_STR], [1], [Define if the default row type is value_timed_str.])
+else
+    AC_MSG_ERROR([$ac_cv_row_type: Unknown row type])
+fi
+
+AC_ARG_ENABLE([max-key-len],
+    [AS_HELP_STRING([--enable-max-key-len=ARG],
+                    [maximum length of a key in bytes, default 255])],
+    [ac_cv_max_key_len=$enableval], [ac_cv_max_key_len=255])
+AC_DEFINE_UNQUOTED([MASSTREE_MAXKEYLEN], [$ac_cv_max_key_len], [Maximum key length])
+
+AC_MSG_CHECKING([whether MADV_HUGEPAGE is supported])
+AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[#include <sys/mman.h>
+#ifndef MADV_HUGEPAGE
+#error "no"
+#endif]], [])],
+                  [have_madv_hugepage=yes], [have_madv_hugepage=no])
+AC_MSG_RESULT([$have_madv_hugepage])
+if test $have_madv_hugepage = yes; then
+    AC_DEFINE([HAVE_MADV_HUGEPAGE], [1], [Define if MADV_HUGEPAGE is supported.])
+fi
+
+AC_MSG_CHECKING([whether MAP_HUGETLB is supported])
+AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[#include <sys/mman.h>
+#ifndef MAP_HUGETLB
+#error "no"
+#endif]], [])],
+                  [have_map_hugetlb=yes], [have_map_hugetlb=no])
+AC_MSG_RESULT([$have_map_hugetlb])
+if test $have_map_hugetlb = yes; then
+    AC_DEFINE([HAVE_MAP_HUGETLB], [1], [Define if MAP_HUGETLB is supported.])
+fi
+
+AC_ARG_ENABLE([superpage],
+    [AS_HELP_STRING([--disable-superpage],
+           [disable superpage support])],
+    [], [enable_superpage=maybe])
+if test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" = "yes no no"; then
+    AC_MSG_ERROR([
+Error: superpages are not supported on this machine.
+Try again without --enable-superpage.
+])
+elif test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" != "maybe no no" -a "$enable_superpage" != no; then
+    AC_DEFINE_UNQUOTED([HAVE_SUPERPAGE], [1], [Define if superpage support is enabled.])
+fi
+
+AC_ARG_ENABLE([memdebug],
+    [AS_HELP_STRING([--enable-memdebug],
+           [enable memory debugging])])
+if test "$enable_memdebug" = yes; then
+    AC_DEFINE_UNQUOTED([HAVE_MEMDEBUG], [1], [Define if memory debugging support is enabled.])
+fi
+
+AC_ARG_ENABLE([assert],
+    [],
+    [AC_MSG_WARN([Use --disable-assertions instead of --disable-assert.])])
+AC_ARG_ENABLE([assertions],
+    [AS_HELP_STRING([--disable-assertions],
+           [disable debugging assertions])])
+if test "$enable_assertions" != no -o "(" -z "$enable_assertions" -a "$enable_assert" != no ")"; then
+    AC_DEFINE_UNQUOTED([ENABLE_ASSERTIONS], [1], [Define to enable debugging assertions.])
+fi
+
+AC_ARG_ENABLE([preconditions],
+    [AS_HELP_STRING([--disable-preconditions],
+            [disable precondition assertions])])
+if test "$enable_preconditions" = no; then
+    AC_DEFINE_UNQUOTED([ENABLE_PRECONDITIONS], [0], [Define to enable precondition assertions.])
+elif test -n "$enable_preconditions"; then
+    AC_DEFINE_UNQUOTED([ENABLE_PRECONDITIONS], [1], [Define to enable precondition assertions.])
+fi
+
+AC_ARG_ENABLE([invariants],
+    [AS_HELP_STRING([--disable-invariants],
+            [disable invariant assertions])])
+if test "$enable_invariants" = no; then
+    AC_DEFINE_UNQUOTED([ENABLE_INVARIANTS], [0], [Define to enable invariant assertions.])
+elif test -n "$enable_preconditions"; then
+    AC_DEFINE_UNQUOTED([ENABLE_INVARIANTS], [1], [Define to enable invariant assertions.])
+fi
+
+AC_DEFINE_UNQUOTED([CACHE_LINE_SIZE], [64], [Assumed size of a cache line.])
+
+AH_TOP([#ifndef MASSTREE_CONFIG_H_INCLUDED
+#define MASSTREE_CONFIG_H_INCLUDED 1])
+
+AH_BOTTOM([#if !FORCE_ENABLE_ASSERTIONS && !ENABLE_ASSERTIONS
+# define NDEBUG 1
+#endif
+
+/** @brief Assert macro that always runs. */
+extern void fail_always_assert(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#define always_assert(x, ...) do { if (!(x)) fail_always_assert(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#define mandatory_assert always_assert
+
+/** @brief Assert macro for invariants.
+
+    masstree_invariant(x) is executed if --enable-invariants or
+    --enable-assertions. */
+extern void fail_masstree_invariant(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_INVARIANTS) && ENABLE_ASSERTIONS) || ENABLE_INVARIANTS
+#define masstree_invariant(x, ...) do { if (!(x)) fail_masstree_invariant(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#else
+#define masstree_invariant(x, ...) do { } while (0)
+#endif
+
+/** @brief Assert macro for preconditions.
+
+    masstree_precondition(x) is executed if --enable-preconditions or
+    --enable-assertions. */
+extern void fail_masstree_precondition(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
+#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_PRECONDITIONS) && ENABLE_ASSERTIONS) || ENABLE_PRECONDITIONS
+#define masstree_precondition(x, ...) do { if (!(x)) fail_masstree_precondition(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
+#else
+#define masstree_precondition(x, ...) do { } while (0)
+#endif
+
+#ifndef invariant
+#define invariant masstree_invariant
+#endif
+#ifndef precondition
+#define precondition masstree_precondition
+#endif
+
+#endif])
+
+AC_DEFINE_UNQUOTED([HAVE_UNALIGNED_ACCESS], [1], [Define if unaligned accesses are OK.])
+
+AC_OUTPUT
diff --git a/silo/masstree/doc/.gitignore b/silo/masstree/doc/.gitignore
new file mode 100644 (file)
index 0000000..d036bee
--- /dev/null
@@ -0,0 +1,11 @@
+*.aux
+*.bbl
+*.blg
+*.dvi
+*.log
+*.mpx
+*_[0-9]*.eps
+*_[0-9]*.pdf
+!GNUmakefile
+mpxerr.tex
+spec.pdf
diff --git a/silo/masstree/doc/GNUmakefile b/silo/masstree/doc/GNUmakefile
new file mode 100644 (file)
index 0000000..501dc0d
--- /dev/null
@@ -0,0 +1,87 @@
+MAIN = spec.tex
+
+BASE := $(basename $(MAIN))
+
+BIBFILES := $(wildcard *.bib)
+STYFILES := $(wildcard *.sty)
+
+GS_FONTPATH := $(shell x=`kpsewhich cmr10.pfb`; echo $$GS_FONTPATH:`dirname $$x`)
+export GS_FONTPATH
+
+#LATEX = pdflatex \\nonstopmode\\input
+USEPDFLATEX = 1
+PDFLATEX = pdflatex
+LATEX = latex
+BIBTEX = bibtex -min-crossrefs=1000
+METAPOST = mpost
+DVIPS := dvips -P pdf -j0 -G0 -t letter
+PS2PDF := ps2pdf
+PS2PDF_LETTER := GS_OPTIONS=-sPAPERSIZE=letter $(PS2PDF) -sPAPERSIZE=letter
+PDF2PS := $(shell (which acroread >/dev/null 2>&1 && echo acroread -toPostScript) || echo pdf2ps)
+
+MPFIGBASE = examples_1 \
+       remove1_1 remove1_2 remove1_3 remove1_31 remove1_4 remove1_5 \
+       remove1_6 remove1_7 remove1_8 remove1_9 \
+       remove2_1 remove2_2 remove2_3 remove2_4 remove2_5 \
+       remove2_6 remove2_7 remove2_8 remove2_9 \
+       remove2_11 remove2_12 remove2_15 remove2_16 \
+       insert1_1 insert1_2 insert1_3 insert1_4 insert1_5
+PDFFIGS += $(addsuffix .pdf,$(MPFIGBASE))
+EPSFIGS += $(addsuffix .eps,$(MPFIGBASE))
+
+RERUN = egrep -q '(^LaTeX Warning:|\(natbib\)).* Rerun'
+UNDEFINED = egrep -q '^(LaTeX|Package natbib) Warning:.* undefined'
+
+RERUNLATEX = if test ! -f $(2).bbl || $(RERUN) $(2).log || $(UNDEFINED) $(2).log; then \
+               set -x; $(BIBTEX) $(2); $(1); \
+               ! $(RERUN) $(2).log || $(1); \
+               ! $(RERUN) $(2).log || $(1); \
+       fi
+
+%.dvi: %.ltx
+       test ! -s $*.aux || $(BIBTEX) $* || rm -f $*.aux $*.bbl
+       $(LATEX) $<
+       @$(call RERUNLATEX,$(LATEX) $<,$*)
+
+%.ps: %.dvi
+       $(DVIPS) $< -o $@
+
+%.pdf: %.ps
+       $(PS2PDF_LETTER) $<
+
+ifeq ($(USEPDFLATEX),1)
+%.pdf: %.tex
+       test ! -s $*.aux || $(BIBTEX) $* || rm -f $*.aux $*.bbl
+       $(PDFLATEX) $<
+       @$(call RERUNLATEX,$(PDFLATEX) $<,$*)
+endif
+
+all: $(BASE).pdf
+
+$(BASE).dvi $(BASE).pdf: $(TEXFILES) $(STYFILES) $(BIBFILES) $(PDFFIGS) $(EPSFIGS)
+
+bib:
+       $(LATEX) $(MAIN)
+       rm -f $(BASE).dvi
+       $(MAKE) $(BASE).dvi
+
+# after making the figure, run perl to fix Computer Modern font names to
+# uppercase
+%_1.eps %_2.eps %_3.eps %_4.eps %_5.eps %_6.eps %_7.eps %_8.eps %_9.eps \
+%_10.eps %_11.eps %_12.eps %_13.eps %_14.eps %_15.eps %_16.eps %_17.eps %_18.eps %_19.eps \
+%_20.eps %_21.eps %_22.eps %_23.eps %_24.eps %_25.eps %_26.eps %_27.eps %_28.eps %_29.eps \
+%_30.eps %_31.eps %_32.eps %_33.eps %_34.eps %_35.eps %_36.eps %_37.eps %_38.eps %_39.eps: \
+               %.mp elements.mp elemfig.sty patches.mp masstree.mp
+       TEX=$(LATEX) $(METAPOST) $<
+       perl -pi -e 'next if $$x; $$x = 1 if /%%EndProlog/; s=/([a-z][a-z0-9]+) def=/\U$$1\E def= if !/^%%/; s=\b([a-z][a-z0-9]+)\b=\U$$1\E=g if /^%%DocumentFonts/ || /^%%\+/;' $(subst .mp,,$<).[0-9]*
+       for i in $(subst .mp,,$<).[0-9]*; do mv $$i `echo "$$i" | sed 's/\.\([0-9]*\)$$/_\1.eps/'`; done
+
+%.pdf: %.eps
+       epstopdf --outfile=$@ $<
+
+clean:
+       rm -f $(EPSFIGS) $(PDFFIGS) $(EPSGRAPHS) $(PDFGRAPHS)
+       rm -f *.ps $(BASE).pdf *.aux
+       rm -f *.dvi *.aux *.log *.bbl *.blg *.lof *.lot *.toc *.bak *~
+
+.PHONY: clean bib all
diff --git a/silo/masstree/doc/elements.mp b/silo/masstree/doc/elements.mp
new file mode 100644 (file)
index 0000000..1161c01
--- /dev/null
@@ -0,0 +1,520 @@
+% elements.mp -- MetaPost macros for drawing Click configuration graphs
+% Eddie Kohler
+%
+% Copyright (c) 1999-2001 Massachusetts Institute of Technology
+% Copyright (c) 2001-2003 International Computer Science Institute
+% Copyright (c) 2006 Regents of the University of California
+%
+% Permission is hereby granted, free of charge, to any person obtaining a
+% copy of this software and associated documentation files (the "Software"),
+% to deal in the Software without restriction, subject to the conditions
+% listed in the Click LICENSE file. These conditions include: you must
+% preserve this copyright notice, and you cannot mention the copyright
+% holders in advertising related to the Software without their permission.
+% The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+% notice is a summary of the Click LICENSE file; the license in that file is
+% legally binding.
+
+input rboxes;
+prologues := 1;
+string defaultelementfont;
+defaultscale := 1;
+linejoin := mitered;
+
+pair element_offset;
+element_offset = (7.5, 4.5);
+min_element_height = 19;
+element_height_increment = 4;
+
+defaultelementborderscale = 1;
+defaultelementportscale = 1;
+
+port_length = 6;
+port_sep = 3;
+port_offset = 4;
+input_length = 7;
+input_width = 4.5;
+output_length = 6;
+output_width = 3.8;
+agnostic_sep = 1;
+
+push = 0;
+pull = 1;
+agnostic = 2;
+agnostic_push = 3;
+agnostic_pull = 4;
+push_to_pull = 5;
+pull_to_push = 6;
+
+pen elementpen.border, elementpen.port, connectionpen;
+elementpen.border = pencircle scaled 0.9;
+elementpen.port = pencircle scaled 0.35;
+connectionpen = pencircle scaled 0.45;
+
+color personalitycolor[], agnosticcolor[];
+personalitycolor[push] = black;
+personalitycolor[agnostic_push] = personalitycolor[agnostic] = white;
+personalitycolor[pull] = personalitycolor[agnostic_pull] = white;
+agnosticcolor[agnostic_push] = black;
+agnosticcolor[agnostic_pull] = white;
+agnosticcolor[agnostic] = 0.6white;
+
+path _agnostic_output, _agnostic_input, _normal_output, _normal_input;
+_agnostic_output := ((-.5,0.5output_length-agnostic_sep)
+  -- (-output_width+agnostic_sep,0.5output_length-agnostic_sep)
+  -- (-output_width+agnostic_sep,-0.5output_length+agnostic_sep)
+  -- (-.5,-0.5output_length+agnostic_sep) -- cycle);
+_agnostic_input := ((.5,0.5input_length-1.414agnostic_sep)
+  -- (input_width-1.414agnostic_sep,0)
+  -- (.5,-0.5input_length+1.414agnostic_sep) -- cycle);
+_normal_input := ((.5,0.5input_length) -- (input_width,0)
+    -- (.5,-0.5input_length) -- cycle);
+_normal_output := ((-.5,0.5output_length) -- (-output_width,0.5output_length)
+    -- (-output_width,-0.5output_length) -- (-.5,-0.5output_length) -- cycle);
+
+
+%% redefine 'drawboxes' to allow extra text
+
+vardef drawboxes(text t) text rest =         % Draw boundary path for each box
+  forsuffixes s=t: draw bpath.s rest; endfor
+enddef;
+
+
+%%
+
+vardef _make_element_ports(suffix $, port, side)(expr n, length, isout) =
+  save _i_, _sc; pair _sc.adj;
+  _sc.sep = (length - 2*port_offset - n*port_length + port_sep) / n;
+  _sc.delta = port_length + _sc.sep;
+  _sc = length/2 - port_offset - (_sc.sep - port_sep)/2 - 0.5port_length;
+  _sc.adj = if isout: 1/2$.flowvector else: -1/2$.flowvector fi;
+  for _i_ = 0 upto n-1:
+    $.port[_i_] = $.side + $.sidevector * (_sc - _sc.delta*_i_) + _sc.adj;
+  endfor;
+enddef;
+
+vardef make_element_inputs(suffix $)(expr xlen, ylen) =
+  if $.down:
+    _make_element_ports($, in, if $.rev: s else: n fi, $.nin, xlen-6, false);
+  else:
+    _make_element_ports($, in, if $.rev: e else: w fi, $.nin, ylen, false);
+  fi;
+enddef;
+
+vardef make_element_outputs(suffix $)(expr xlen, ylen) =
+  if $.down:
+    _make_element_ports($, out, if $.rev: n else: s fi, $.nout, xlen-6, true);
+  else:
+    _make_element_ports($, out, if $.rev: w else: e fi, $.nout, ylen, true);
+  fi;
+enddef;
+
+vardef clearelement_(suffix $) =
+  _n_ := str $;
+  generic_redeclare(numeric) _n.down, _n.rev, _n.sidevector, _n.flowvector, _n.width, _n.height, _n.nin, _n.nout, _n.borderscale, _n.portscale, _n.drawports;
+  _n_ := str $ & ".in0";
+  generic_redeclare(numeric) _n;
+  _n_ := str $ & ".out0";
+  generic_redeclare(numeric) _n;
+  _n_ := str $ & ".inpers0";
+  generic_redeclare(numeric) _n;
+  _n_ := str $ & ".outpers0";
+  generic_redeclare(numeric) _n;
+  _n_ := "elemdraw_." & str $;
+  generic_redeclare(numeric) _n;
+enddef;
+
+vardef _elementit@#(expr label_str, ninputs, noutputs, personality, down_var, rev_var) =
+  picture _label_; numeric _x_, _y_;
+  
+  if picture label_str: _label_ = label_str
+  elseif label_str = "": _label_ = nullpicture
+  else: _label_ = label_str infont defaultelementfont scaled defaultscale
+  fi;
+  
+  boxit.@#(_label_);
+  _n_ := str @#;
+  generic_declare(boolean) _n.down, _n.rev, _n.drawports;
+  generic_declare(pair) _n.sidevector, _n.flowvector;
+  generic_declare(numeric) _n.width, _n.height, _n.nin, _n.nout, _n.borderscale, _n.portscale;
+  _n_ := str @# & ".in0";
+  generic_declare(pair) _n;
+  _n_ := str @# & ".out0";
+  generic_declare(pair) _n;
+  _n_ := "elemdraw_." & str @#;
+  generic_declare(string) _n;
+  
+  @#.down = down_var;
+  if down_var: @#.sidevector = (-1, 0); else: @#.sidevector = (0, 1); fi;
+  if down_var: @#.flowvector = (0, -1); else: @#.flowvector = (1, 0); fi;
+  @#.rev = rev_var;
+  if rev_var: @#.flowvector := -@#.flowvector; @#.sidevector := -@#.sidevector; fi;
+  @#.drawports = true;
+  
+  @#.width = xpart(@#.e - @#.w);
+  @#.height = ypart(@#.n - @#.s);
+  
+  @#.nin = ninputs;
+  @#.nout = noutputs;
+  if ninputs > 0: make_element_inputs(@#, @#.width, @#.height); fi;
+  if noutputs > 0: make_element_outputs(@#, @#.width, @#.height); fi;
+  
+  _x_ := personality;
+  if _x_ = push_to_pull: _x_ := push;
+  elseif _x_ = pull_to_push: _x_ := pull; fi;
+  for _y_ = 0 upto ninputs-1: @#.inpers[_y_] = _x_; endfor;
+  
+  _x_ := personality;
+  if _x_ = push_to_pull: _x_ := pull;
+  elseif _x_ = pull_to_push: _x_ := push; fi;
+  for _y_ = 0 upto noutputs-1: @#.outpers[_y_] = _x_; endfor;
+  
+  elemdraw_@# = "drawboxes";
+  sproc_@# := "sizeelement_";
+  
+  expandafter def expandafter clearboxes expandafter =
+    clearboxes clearelement_(@#);
+  enddef
+enddef;
+
+vardef elementit@#(expr s, ninputs, noutputs, personality_var) =
+  _elementit.@#(s, ninputs, noutputs, personality_var, false, false);
+enddef;
+vardef relementit@#(expr s, ninputs, noutputs, personality_var) =
+  _elementit.@#(s, ninputs, noutputs, personality_var, false, true);
+enddef;
+vardef velementit@#(expr s, ninputs, noutputs, personality_var) =
+  _elementit.@#(s, ninputs, noutputs, personality_var, true, false);
+enddef;
+vardef rvelementit@#(expr s, ninputs, noutputs, personality_var) =
+  _elementit.@#(s, ninputs, noutputs, personality_var, true, true);
+enddef;
+
+
+%% change
+
+vardef killinput(suffix $)(expr p) =
+  if (p >= 0) and (p < $.nin): save _i_;
+    for _i_ = p upto $.nin-2:
+      $.in[_i_] := $.in[_i_+1];
+      $.inpers[_i_] := $.inpers[_i_+1];
+    endfor;
+    $.nin := $.nin - 1
+  fi
+enddef;
+
+vardef killoutput(suffix $)(expr p) =
+  if (p >= 0) and (p < $.nout): save _i_;
+    for _i_ = p upto $.nout-2:
+      $.out[_i_] := $.out[_i_+1];
+      $.outpers[_i_] := $.outpers[_i_+1];
+    endfor;
+    $.nout := $.nout - 1
+  fi
+enddef;
+
+vardef portinteriorin(suffix $)(expr i) =
+  path _p_;
+  _p_ := if $.inpers[i] >= agnostic: _agnostic_input else: _normal_input fi
+    scaled $.portscale;
+  if $.down: _p_ := _p_ rotated -90 fi;
+  if $.rev: _p_ := _p_ rotated 180 fi;
+  _p_ := _p_ shifted $.in[i];
+  if $.down and $.rev: .5[ulcorner _p_,urcorner _p_]
+  elseif $.down: .5[llcorner _p_,lrcorner _p_]
+  elseif $.rev: .5[ulcorner _p_,llcorner _p_]
+  else: .5[urcorner _p_,lrcorner _p_] fi
+enddef;
+
+vardef portinteriorout(suffix $)(expr i) =
+  path _p_;
+  _p_ := if $.outpers[i] >= agnostic: _agnostic_output else: _normal_output fi
+    scaled $.portscale;
+  if $.down: _p_ := _p_ rotated -90 fi;
+  if $.rev: _p_ := _p_ rotated 180 fi;
+  _p_ := _p_ shifted $.out[i];
+  if $.down and $.rev: .5[llcorner _p_,lrcorner _p_]
+  elseif $.down: .5[ulcorner _p_,urcorner _p_]
+  elseif $.rev: .5[urcorner _p_,lrcorner _p_]
+  else: .5[ulcorner _p_,llcorner _p_] fi
+enddef;
+
+
+%% fix
+
+vardef set_element_dx(suffix $) =
+  if $.down: save x;
+    x.maxport = max($.nin, $.nout);
+    x.len = x.maxport*port_length + (x.maxport-1)*port_sep + 2port_offset;
+    x.w = xpart(urcorner pic_$ - llcorner pic_$);
+    x.ww = x.w + 2xpart(element_offset);
+    if x.len > x.ww: $.dx = (x.len - x.w) / 2;
+    else: $.dx = xpart element_offset; fi;
+  else:
+    $.dx = xpart element_offset;
+  fi;
+enddef;
+
+vardef set_element_dy(suffix $) =
+  save y;
+  y.h = ypart(urcorner pic_$ - llcorner pic_$);
+  y.hh = y.h + 2ypart(element_offset);
+  if $.down: y := y.hh;
+  else:
+    y.maxport = max($.nin, $.nout);
+    y.len = y.maxport*port_length + (y.maxport-1)*port_sep + 2port_offset;
+    y := max(y.hh, y.len);
+  fi;
+  
+  y'' := min_element_height;
+  forever:
+    exitif y'' >= y;
+    y'' := y'' + element_height_increment;
+  endfor;
+  
+  $.dy = (y'' - y.h)/2;
+enddef;
+
+def sizeelement_(suffix $) =
+  if unknown $.dx: set_element_dx($); fi
+  if unknown $.dy: set_element_dy($); fi
+  if unknown $.borderscale: $.borderscale = defaultelementborderscale; fi
+  if unknown $.portscale: $.portscale = defaultelementportscale; fi
+enddef;
+
+vardef fixelementsizeleft(text t) =
+  forsuffixes $=t:
+    fixsize($);
+    if $.dx > xpart element_offset: $.off := $.off - ($.dx - xpart element_offset, 0); fi;
+  endfor;
+enddef;
+
+vardef fixelement(text elements) =
+  fixsize(elements);
+  fixpos(elements);
+enddef;
+
+vardef elementleftjustify(text elements) =
+  fixsize(elements);
+  forsuffixes $=elements:
+    if $.dx > xpart element_offset: $.off := $.off - ($.dx - xpart element_offset, 0); fi;
+  endfor;
+enddef;
+
+vardef fixrelations(suffix efirst)(text elements) =
+  forsuffixes $=elements:
+    if unknown xpart(efirst.off - $.off): xpart $.off = xpart efirst.off fi;
+    if unknown ypart(efirst.off - $.off): ypart $.off = ypart efirst.off fi;
+  endfor;
+enddef;
+
+vardef elementbbox(suffix efirst)(text elements) =
+  fixsize(efirst,elements);
+  fixrelations(efirst,elements);
+  save __t,__l,__r,__b,__p; picture __p;
+  __t = ypart(efirst.n - efirst.off);
+  __l = xpart(efirst.w - efirst.off);
+  __r = xpart(efirst.e - efirst.off);
+  __b = ypart(efirst.s - efirst.off);
+  forsuffixes $=elements:
+    if ypart($.n - efirst.off) > __t: __t := ypart($.n - efirst.off) fi;
+    if xpart($.w - efirst.off) < __l: __l := xpart($.w - efirst.off) fi;
+    if xpart($.e - efirst.off) > __r: __r := xpart($.e - efirst.off) fi;
+    if ypart($.s - efirst.off) < __b: __b := ypart($.s - efirst.off) fi;
+  endfor;
+  __p = nullpicture;
+  setbounds __p to ((__l,__t) -- (__r,__t) -- (__r,__b) -- (__l,__b) -- cycle)
+     if known efirst.off: shifted efirst.off fi;
+  __p
+enddef;
+
+vardef compoundelementlink@#(suffix efirst)(text elements) =
+  fixsize(efirst,elements);
+  fixrelations(efirst,elements);
+  save __t,__l,__r,__b,__p; picture __p;
+  __t = ypart(efirst.n - efirst.off);
+  __l = xpart(efirst.w - efirst.off);
+  __r = xpart(efirst.e - efirst.off);
+  __b = ypart(efirst.s - efirst.off);
+  forsuffixes $=elements:
+    if ypart($.n - efirst.off) > __t: __t := ypart($.n - efirst.off) fi;
+    if xpart($.w - efirst.off) < __l: __l := xpart($.w - efirst.off) fi;
+    if xpart($.e - efirst.off) > __r: __r := xpart($.e - efirst.off) fi;
+    if ypart($.s - efirst.off) < __b: __b := ypart($.s - efirst.off) fi;
+  endfor;
+  @#.c = efirst.off + .5[(__l,__t), (__r,__b)]
+enddef;
+
+
+%% draw
+
+vardef draw_element_inputs(suffix $) =
+  path _p_, _ag_;
+  _p_ := _normal_input scaled $.portscale;
+  _ag_ := _agnostic_input scaled $.portscale;
+  if $.down: _p_ := _p_ rotated -90; _ag_ := _ag_ rotated -90; fi;
+  if $.rev: _p_ := _p_ rotated 180; _ag_ := _ag_ rotated 180; fi;
+  for _i_ = 0 upto $.nin - 1:
+    if $.inpers[_i_] >= 0:
+      fill _p_ shifted $.in[_i_] withcolor personalitycolor[$.inpers[_i_]];
+      draw _p_ shifted $.in[_i_];
+      if $.inpers[_i_] >= agnostic:
+       fill _ag_ shifted $.in[_i_] withcolor agnosticcolor[$.inpers[_i_]];
+       draw _ag_ shifted $.in[_i_]; fi
+    fi;
+  endfor
+enddef;
+
+vardef draw_element_outputs(suffix $) =
+  path _p_, _ag_;
+  _p_ := _normal_output scaled $.portscale;
+  _ag_ := _agnostic_output scaled $.portscale;
+  if $.down: _p_ := _p_ rotated -90; _ag_ := _ag_ rotated -90; fi;
+  if $.rev: _p_ := _p_ rotated 180; _ag_ := _ag_ rotated 180; fi;
+  for _i_ = 0 upto $.nout - 1:
+    if $.outpers[_i_] >= 0:
+      fill _p_ shifted $.out[_i_] withcolor personalitycolor[$.outpers[_i_]];
+      draw _p_ shifted $.out[_i_];
+      if $.outpers[_i_] >= agnostic:
+       fill _ag_ shifted $.out[_i_] withcolor agnosticcolor[$.outpers[_i_]];
+       draw _ag_ shifted $.out[_i_]; fi
+    fi;
+  endfor
+enddef;
+
+vardef drawelement(text elements) text rest =
+  drawelementbox(elements) rest;
+  drawunboxed(elements);
+enddef;
+
+vardef drawelementbox(text elements) text rest =
+  save $, oldpen; oldpen := savepen;
+  interim linejoin := mitered;
+  fixsize(elements);
+  fixpos(elements);
+  forsuffixes $ = elements:
+    if $.drawports:
+      pickup elementpen.port scaled $.portscale;
+      if $.nin > 0: draw_element_inputs($); fi;
+      if $.nout > 0: draw_element_outputs($); fi;
+    fi;
+    if $.borderscale > 0:
+      pickup elementpen.border scaled $.borderscale;
+      scantokens elemdraw_$($) rest;
+    fi;
+  endfor;
+  pickup oldpen;
+enddef;
+
+vardef fillelement(text elements)(text color) =
+  fixsize(elements);
+  fixpos(elements);
+  forsuffixes $=elements:
+    fill bpath.$ withcolor color;
+  endfor;
+enddef;
+
+
+%% queues
+
+vardef _drawqueued(expr p,delta,rot,lim,pp) text rest =
+  save i; interim linecap := squared; i := delta;
+  forever:
+    draw (p) shifted ((i,0) rotated rot) withpen currentpen scaled 0.25 rest;
+    i := i + delta; exitunless i < lim;
+  endfor;
+  draw (pp) rest;
+enddef;
+def drawqueued(suffix $) =
+  _drawqueued($.ne -- $.se, 6, 180, .9*$.width, $.nw -- $.ne -- $.se -- $.sw)
+enddef;
+def drawrqueued(suffix $) =
+  _drawqueued($.nw -- $.sw, 6, 0, .9*$.width, $.ne -- $.nw -- $.sw -- $.se)
+enddef;
+def drawvqueued(suffix $) =
+  _drawqueued($.se -- $.sw, 5, 90, .9*$.height, $.nw -- $.sw -- $.se -- $.ne)
+enddef;
+def drawrvqueued(suffix $) =
+  _drawqueued($.ne -- $.nw, 5, 270, .9*$.height, $.sw -- $.nw -- $.ne -- $.se)
+enddef;
+
+vardef queueit@#(expr s) =
+  _elementit.@#(s, 1, 1, push_to_pull, false, false);
+  elemdraw_@# := "drawqueued";
+enddef;
+vardef rqueueit@#(expr s) =
+  _elementit.@#(s, 1, 1, push_to_pull, false, true);
+  elemdraw_@# := "drawrqueued";
+enddef;
+vardef vqueueit@#(expr s) =
+  _elementit.@#(s, 1, 1, push_to_pull, true, false);
+  elemdraw_@# := "drawvqueued";
+enddef;
+vardef rvqueueit@#(expr s) =
+  _elementit.@#(s, 1, 1, push_to_pull, true, true);
+  elemdraw_@# := "drawrvqueued";
+enddef;
+
+
+%% connections
+
+picture _cutarrpic;
+
+vardef arrowhead expr p =
+  save q,h,e,f; path q,h; pair e,f;
+  e = point length p of p;
+  q = gobble(p shifted -e cutafter makepath(pencircle scaled 2ahlength))
+    cuttings;
+  h = gobble(p shifted -e cutafter makepath(pencircle scaled 1.5ahlength))
+    cuttings;
+  f = point 0 of h;
+  (q rotated .5ahangle & reverse q rotated -.5ahangle -- f -- cycle)  shifted e
+enddef;
+def _cutarr(expr b,e) text t =
+  _cutarrpic := image(draw (0,0) -- (1,0) -- cycle t);
+  _cutarramt := (2ypart urcorner _cutarrpic / sind .5ahangle) - 0.75;
+  if _cutarramt > 0:
+    _apth := subpath (xpart(_apth intersectiontimes makepath(pencircle
+         scaled (b*_cutarramt)) shifted (point 0 of _apth)),
+      xpart(_apth intersectiontimes makepath(pencircle scaled (e*_cutarramt))
+  shifted (point length _apth of _apth))) of _apth;
+  fi
+enddef;
+def _finarr text t =
+  _cutarr(0,1) t;
+  draw (subpath (0, xpart(_apth intersectiontimes makepath(pencircle scaled 1.2ahlength) shifted (point length _apth of _apth))) of _apth) t;
+  fill arrowhead _apth  t
+enddef;
+def _findarr text t =
+  _cutarr(1,1) t;
+  draw (subpath
+    (xpart(_apth intersectiontimes makepath(pencircle scaled 1.2ahlength) shifted (point 0 of _apth)),
+     xpart(_apth intersectiontimes makepath(pencircle scaled 1.2ahlength) shifted (point length _apth of _apth))) of _apth) t;
+  fill arrowhead _apth  t;
+  fill arrowhead reverse _apth  t
+enddef;
+
+def connectpath(suffix $,#,##,$$) =
+  $.out[#]{$.flowvector} .. {$$.flowvector}$$.in[##]
+enddef;
+vardef drawconnectj(suffix $,#,##,$$)(text t) text rest =
+  interim linejoin := mitered;
+  drawarrow $.out[#]{$.flowvector} t {$$.flowvector}$$.in[##] withpen connectionpen rest
+enddef;
+def drawconnect(suffix $,#,##,$$) =
+  drawconnectj($,#,##,$$)(..)
+enddef;
+vardef drawconnectna(suffix $,#,##,$$) text rest =
+  interim linejoin := mitered;
+  draw $.out[#]{$.flowvector} .. {$$.flowvector}$$.in[##] withpen connectionpen rest
+enddef;
+
+def drawconnarrow expr p =
+  _apth:=p; _finarr withpen connectionpen
+enddef;
+def drawconnarrowna expr p =
+  draw p withpen connectionpen
+enddef;
+def drawdblconnarrow expr p =
+  _apth:=p; _findarr withpen connectionpen
+enddef;
diff --git a/silo/masstree/doc/elemfig.sty b/silo/masstree/doc/elemfig.sty
new file mode 100644 (file)
index 0000000..48bec23
--- /dev/null
@@ -0,0 +1,88 @@
+% elemfig.sty -- LaTeX macros for Click configuration graphs in MetaPost
+% Eddie Kohler
+%
+% Copyright (c) 1999-2001 Massachusetts Institute of Technology
+% Copyright (c) 2001-2003 International Computer Science Institute
+%
+% Permission is hereby granted, free of charge, to any person obtaining a
+% copy of this software and associated documentation files (the "Software"),
+% to deal in the Software without restriction, subject to the conditions
+% listed in the Click LICENSE file. These conditions include: you must
+% preserve this copyright notice, and you cannot mention the copyright
+% holders in advertising related to the Software without their permission.
+% The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+% notice is a summary of the Click LICENSE file; the license in that file is
+% legally binding.
+
+\makeatletter
+\usepackage{times,mathptmx}
+\usepackage[T1]{fontenc}
+%\input{../notation}
+
+\IfFileExists{./pfonts.sty}{\input{./pfonts.sty}}{%
+  \IfFileExists{../pfonts.sty}{\input{../pfonts.sty}}{%
+    \input{times.sty}%
+  }%
+}%
+\@ifundefined{elementdefault}{}{\long\global\edef\rmdefault{\elementdefault}}
+
+% for MetaPost
+
+\renewcommand{\v}[1]{\ifmmode\mathit{#1\strut}\else\textit{#1}\fi}
+
+\let\elementshape\itshape
+\newdimen\@elementQdp\setbox0\hbox{\elementshape Q}\@elementQdp=\dp0
+\def\element#1{{\setbox0\hbox{\elementshape #1\vphantom{Q}}%
+       \dimen0=\dp0\advance\dimen0 by-0.5\@elementQdp\dp0=\dimen0\box0}}%
+\def\belement#1{\textbf{\element{#1}}}%
+\newcommand{\elementlabel}[1]{\element{#1}}
+
+\newcommand{\melementlabel}[1]{{%
+  \elementshape
+  \begin{tabular}{@{}c@{}}%
+  #1
+  \end{tabular}%
+}}%
+\newcommand{\lmelementlabel}[1]{{%
+  \elementshape
+  \begin{tabular}{@{}l@{}}%
+  #1
+  \end{tabular}%
+}}%
+\newcommand{\rmelementlabel}[1]{{%
+  \elementshape
+  \begin{tabular}{@{}r@{}}%
+  #1
+  \end{tabular}%
+}}%
+\let\lelementlabel\lmelementlabel
+\let\relementlabel\rmelementlabel
+\newcommand{\mlabel}[1]{{%
+  \begin{tabular}{@{}c@{}}%
+  #1
+  \end{tabular}%
+}}%
+\newcommand{\lmlabel}[1]{{%
+  \begin{tabular}{@{}l@{}}%
+  #1
+  \end{tabular}%
+}}%
+\newcommand{\rmlabel}[1]{{%
+  \begin{tabular}{@{}r@{}}%
+  #1
+  \end{tabular}%
+}}%
+\let\llabel\lmlabel
+\let\rlabel\rmlabel
+\newcommand{\portlabel}[1]{\hbox{%
+  \tiny\sffamily\baselineskip6pt\begin{tabular}{@{}c@{}}#1\end{tabular}%
+}}%
+\newcommand{\lportlabel}[1]{\hbox{%
+  \tiny\sffamily\baselineskip6pt\begin{tabular}{@{}l@{}}#1\end{tabular}%
+}}%
+\newcommand{\rportlabel}[1]{\hbox{%
+  \tiny\sffamily\baselineskip6pt\begin{tabular}{@{}r@{}}#1\end{tabular}%
+}}%
+
+\makeatother
+\endinput
diff --git a/silo/masstree/doc/examples.mp b/silo/masstree/doc/examples.mp
new file mode 100644 (file)
index 0000000..103e15c
--- /dev/null
@@ -0,0 +1,53 @@
+input masstree;
+verbatimtex %&latex
+\documentclass[12pt]{article}
+\usepackage{elemfig,amsmath}
+\begin{document}
+\newcommand{\xlab}[1]{\mlabel{\textsc{#1}}}
+etex;
+
+picture data[];
+data[0] = btex \xlab{a} etex; data[1] = btex \xlab{b} etex; data[2] = btex \xlab{c} etex;
+data[3] = btex \xlab{d} etex; data[4] = btex \xlab{e} etex; data[5] = btex \xlab{f} etex;
+data[6] = btex \xlab{g} etex; data[7] = btex \xlab{h} etex; data[8] = btex \xlab{i} etex;
+data[9] = btex \xlab{j} etex; data[10] = btex \xlab{k} etex; data[11] = btex \xlab{l} etex;
+data[12] = btex \xlab{m} etex; data[13] = btex \xlab{n} etex; data[14] = btex \xlab{o} etex;
+
+pair min_reasonable_cell; min_reasonable_cell := (0,0);
+for _i_ = 0 upto 14:
+  min_reasonable_cell :=
+    (max(xpart min_reasonable_cell, xpart (urcorner data[_i_] - llcorner data[_i_])),
+      max(ypart min_reasonable_cell, ypart (urcorner data[_i_] - llcorner data[_i_])));
+endfor;
+color deletedelement; deletedelement = .5white;
+
+picture emptyqueue; emptyqueue := btex \phantom{\elementlabel{Queue}} etex;
+picture vemptyqueue; vemptyqueue := emptyqueue rotated 90 xscaled .4;
+hardborderscale = 3;
+
+beginfig(1);
+  % initial tree
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  leafconnect(a,d,g,j,m);
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+  label.lft(btex \small{(1)~~} etex, inj.w);
+endfig;
+
+end
diff --git a/silo/masstree/doc/insert1.mp b/silo/masstree/doc/insert1.mp
new file mode 100644 (file)
index 0000000..5c2b9aa
--- /dev/null
@@ -0,0 +1,179 @@
+input masstree;
+verbatimtex %&latex
+\documentclass[12pt]{article}
+\usepackage{elemfig,amsmath}
+\begin{document}
+\newcommand{\xlab}[1]{\mlabel{\textsc{#1}}}
+etex;
+
+picture data[];
+data[0] = btex \xlab{a} etex; data[1] = btex \xlab{b} etex; data[2] = btex \xlab{c} etex;
+data[3] = btex \xlab{d} etex; data[4] = btex \xlab{e} etex; data[5] = btex \xlab{f} etex;
+data[6] = btex \xlab{g} etex; data[7] = btex \xlab{h} etex; data[8] = btex \xlab{i} etex;
+data[9] = btex \xlab{j} etex; data[10] = btex \xlab{k} etex; data[11] = btex \xlab{l} etex;
+data[12] = btex \xlab{m} etex; data[13] = btex \xlab{n} etex; data[14] = btex \xlab{o} etex;
+picture dotdata;
+dotdata = btex \xlab{\dots\dots} etex;
+
+pair min_reasonable_cell; min_reasonable_cell := (0,0);
+for _i_ = 0 upto 14:
+  min_reasonable_cell :=
+    (max(xpart min_reasonable_cell, xpart (urcorner data[_i_] - llcorner data[_i_])),
+      max(ypart min_reasonable_cell, ypart (urcorner data[_i_] - llcorner data[_i_])));
+endfor;
+color deletedelement; deletedelement = .5white;
+color upperlayer, upperlayerfill; upperlayer = (0,0,.5); upperlayerfill = (.9,.9,1);
+
+picture emptyqueue; emptyqueue := btex \phantom{\elementlabel{Queue}} etex;
+picture vemptyqueue; vemptyqueue := emptyqueue rotated 90 xscaled .4;
+hardborderscale = 3;
+
+beginfig(1);
+  % tree with MNO removed, gclayer begin
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.nextpath = x.prevpath = emptypath;
+  internalnodeit.ina(data[3], data[6], data[9]);
+  ina.s = .5[a.nw,j.ne] + (0,24);
+  .5[x.sw,x.se] = ina.n + (0,24);
+  fixelement(a,d,g,j,ina,x);
+  leafconnect(a,d,g,j);
+  drawelement(a,d,g,j,ina);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} ina.n withpen connectionpen scaled 2 withcolor upperlayer;
+  internalnodeconnect(ina,a,d,g,j);
+endfig;
+
+beginfig(2);
+  % tree with MNO removed, gclayer begin
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], nullpicture, nullpicture);
+  j.locked = m.locked = true;
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.nextpath = x.prevpath = emptypath;
+  internalnodeit.ina(data[3], data[6], data[9]);
+  ina.s = .5[a.nw,j.ne] + (0,24);
+  .5[x.sw,x.se] = ina.n + (0,24);
+  fixelement(a,d,g,j,m,ina,x);
+  leafconnect(a,d,g,j,m);
+  drawelement(a,d,g,j,m,ina);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} ina.n withpen connectionpen scaled 2 withcolor upperlayer;
+  internalnodeconnect(ina,a,d,g,j);
+endfig;
+
+beginfig(3);
+  % tree with MNO removed, gclayer begin
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], nullpicture, nullpicture);
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.nextpath = x.prevpath = emptypath;
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  ina.locked = inj.locked = true;
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  .5[x.sw,x.se] = ina.n + (0,24);
+  fixelement(a,d,g,j,m,ina,inj,x);
+  leafconnect(a,d,g,j,m);
+  drawelement(a,d,g,j,m,ina,inj);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} ina.n withpen connectionpen scaled 2 withcolor upperlayer;
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+endfig;
+
+beginfig(4);
+  % tree with MNO removed, gclayer begin
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], nullpicture, nullpicture);
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.nextpath = x.prevpath = emptypath;
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.sw = .3[ina.n,inj.n] + (0,24);
+  .8[x.sw,x.se] = root.n + (0,24);
+  fixelement(a,d,g,j,m,root,ina,inj,x);
+  leafconnect(a,d,g,j,m);
+  drawelement(a,d,g,j,m,ina,inj,root);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} ina.n withpen connectionpen scaled 2 withcolor upperlayer;
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  ina.connectin := .6[ina.nw,ina.ne];
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(5);
+  % tree with MNO removed, gclayer begin
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], nullpicture, nullpicture);
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.locked = true;
+  x.nextpath = x.prevpath = emptypath;
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.sw = .3[ina.n,inj.n] + (0,24);
+  .5[x.sw,x.se] = root.n + (0,24);
+  fixelement(a,d,g,j,m,root,ina,inj,x);
+  leafconnect(a,d,g,j,m);
+  drawelement(a,d,g,j,m,ina,inj,root);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} root.n withpen connectionpen scaled 2 withcolor upperlayer;
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+end
diff --git a/silo/masstree/doc/masstree.mp b/silo/masstree/doc/masstree.mp
new file mode 100644 (file)
index 0000000..d813505
--- /dev/null
@@ -0,0 +1,200 @@
+input patches;
+
+def emptypath =
+  subpath (0,0) of (0,0) -- (0,0)
+enddef;
+
+vardef clearnode_(suffix $) =
+  if $.isleaf:
+    _n_ := str $;
+    generic_redeclare(numeric) _n.prev, _n.previn, _n.next, _n.nextin, _n.nextpath, _n.prevpath,
+      _n.nextbpath, _n.prevbpath;
+    _n_ := str $ & ".value0";
+    generic_redeclare(numeric) _n;
+  else:
+    _n_ := str $ & ".child0";
+    generic_redeclare(numeric) _n;
+  fi
+  _n_ := str $;
+  generic_redeclare(numeric) _n.linkwidth, _n.nkeys, _n.deleted, _n.isleaf, _n.locked, _n.keywidth, _n.connectin;
+  _n_ := str $ & ".keypic0";
+  generic_redeclare(numeric) _n;
+enddef;
+
+vardef drawnode_(suffix $) text rest =
+  save _i_, _w_, _pos_, _ll_; numeric _i_, _w_; pair _pos_, _ll_;
+  _ll_ = $.sw + ($.linkwidth,0); _w_ = $.keywidth;
+  for _i_ = 0 upto $.nkeys-1:
+    if xpart (llcorner $.keypic[_i_] - urcorner $.keypic[_i_]) = 0:
+      fill ((0,0) -- (_w_,0) -- (_w_,$.height) -- (0,$.height) -- cycle) shifted (_ll_ + (_w_*_i_,0)) withcolor .9white;
+    fi
+  endfor
+  if $.locked:
+    begingroup interim linecap := butt;
+      draw ((0,-3) {left} .. (-3,0) {up} .. (0,3) {right} .. {down} (3,0)) shifted $.nw withpen pencircle scaled 2 rest;
+    endgroup;
+  fi
+  drawboxes($) rest;
+  if $.isleaf:
+    draw ($.nw + ($.linkwidth,0)) -- ($.sw + ($.linkwidth,0)) withpen connectionpen rest;
+    if known $.nextpath:
+      drawarrow $.nextpath dashed withdots scaled 0.5 rest;
+    else:
+      draw $.ne -- ($.se - ($.linkwidth,0)) withpen connectionpen scaled 0.2 rest;
+    fi
+    draw ($.ne - ($.linkwidth,0)) -- ($.se - ($.linkwidth,0)) withpen connectionpen rest;
+    if known $.prevpath:
+      drawarrow $.prevpath dashed withdots scaled 0.5 rest;
+    else:
+      draw ($.nw + ($.linkwidth,0)) -- $.sw withpen connectionpen scaled 0.5 rest;
+    fi
+  fi
+  for _i_ = 0 upto $.nkeys-1:
+    _pos_ := .5[llcorner $.keypic[_i_],urcorner $.keypic[_i_]];
+    draw $.keypic[_i_] shifted (_ll_ + (_w_*(_i_+0.5),.5*$.height) - _pos_) rest;
+    if _i_ > 0:
+      draw (_ll_ + (_w_*_i_,0)) -- (_ll_ + (_w_*_i_,.15*$.height)) withpen connectionpen scaled 2 rest;
+      draw (_ll_ + (_w_*_i_,$.height)) -- (_ll_ + (_w_*_i_,.85*$.height)) withpen connectionpen scaled 2 rest;
+    fi
+  endfor
+  if $.deleted:
+    draw ($.nw + .1*($.se-$.nw)) -- ($.se - .1*($.se-$.nw))
+      withpen penrazor scaled 5 rotated 60 withcolor white;
+    draw ($.sw - .1*($.sw-$.ne)) -- ($.ne + .1*($.sw-$.ne))
+      withpen penrazor scaled 5 rotated -60 withcolor white;
+  fi
+enddef;
+
+vardef sizenode_(suffix $) =
+  if unknown $.linkwidth: $.linkwidth = if $.isleaf: 5 else: 0 fi; fi
+  if unknown $.deleted: $.deleted = false; fi
+  if unknown $.locked: $.locked = false; fi
+  if unknown $.dx or unknown $.dy:
+    save _a_, _i_; pair _a_; _a_ := min_reasonable_cell;
+    for _i_ = 0 upto $.nkeys-1:
+      _a_ := (max(xpart _a_,xpart (urcorner $.keypic[_i_] - llcorner $.keypic[_i_])),
+         max(ypart _a_,ypart (urcorner $.keypic[_i_] - llcorner $.keypic[_i_])));
+    endfor
+    if unknown $.dx: $.dx = ($.linkwidth*2 + (2*xpart element_offset + xpart _a_)*$.nkeys)/2; fi
+    if unknown $.dy: $.dy = (2*ypart element_offset + ypart _a_)/2; fi
+  fi
+  sizeelement_($)
+enddef;
+
+vardef leafit@#(text components) =
+  _elementit.@#(nullpicture, 0, 0, push, false, false);
+  _n_ := str @#;
+  generic_declare(numeric) _n.linkwidth, _n.nkeys, _n.keywidth;
+  generic_declare(boolean) _n.deleted, _n.isleaf, _n.locked;
+  generic_declare(pair) _n.prev, _n.previn, _n.next, _n.nextin, _n.connectin;
+  generic_declare(path) _n.prevpath, _n.nextpath, _n.nextbpath, _n.prevbpath;
+  _n_ := str @# & ".keypic0";
+  generic_declare(picture) _n;
+  _n_ := str @# & ".value0";
+  generic_declare(pair) _n;
+  @#.isleaf = true;
+  save _i_, _nk_; picture _i_; _nk_ := 0;
+  for _i_ = components:
+    @#.keypic[_nk_] = _i_;
+    @#.value[_nk_] = @#.sw + (@#.linkwidth + @#.keywidth*(_nk_+0.5),.15*@#.height);
+    _nk_ := _nk_ + 1;
+  endfor
+  elemdraw_@# := "drawnode_";
+  sproc_@# := "sizenode_";
+  @#.prev = @#.w + (@#.linkwidth/2,-@#.height/10);
+  @#.previn = @#.e + (0,-@#.height/10);
+  @#.next = @#.e + (-@#.linkwidth/2,@#.height/10);
+  @#.nextin = @#.w + (0,@#.height/10);
+  @#.nkeys = _nk_;
+  @#.keywidth = (@#.width - 2 * @#.linkwidth) / @#.nkeys;
+  expandafter def expandafter clearboxes expandafter =
+    clearboxes clearnode_(@#);
+  enddef
+enddef;
+
+vardef leafconnectnext(suffix $,$$)(text connection) =
+  $.nextpath = $.next connection $$.nextin;
+enddef;
+vardef leafconnectprev(suffix $,$$)(text connection) =
+  $.prevpath = $.prev connection $$.previn;
+enddef;
+vardef leafconnect(text suffixes) =
+  save _fart_; string _fart_;
+  forsuffixes $=suffixes:
+    if known _fart_:
+      leafconnectnext(scantokens _fart_,$)(--);
+      leafconnectprev($,scantokens _fart_)(--);
+    fi
+    _fart_ := str $;
+  endfor
+enddef;
+def leafnextbpath(suffix $) =
+  $.ne -- ($.ne - ($.linkwidth,0)) -- ($.se - ($.linkwidth,0)) -- $.se -- cycle
+enddef;
+def leafprevbpath(suffix $) =
+  $.nw -- ($.nw + ($.linkwidth,0)) -- ($.sw + ($.linkwidth,0)) -- $.sw -- cycle
+enddef;
+def nodecellsbpath(suffix $)(expr i,j) =
+  (((0,0) -- ((j-i+1)*$.keywidth,0) -- ((j-i+1)*$.keywidth,$.height) -- (0,$.height) -- cycle)
+      shifted ($.sw + ($.linkwidth + i*$.keywidth,0)))
+enddef;
+def nodecellbpath(suffix $)(expr i) =
+  nodecellsbpath($,i,i)
+enddef;
+
+def internalnodeconnectone(suffix $,$$)(expr i) =
+  if known $$.connectin:
+    drawconnarrow $.child[i] {down} .. tension 4 .. {down} $$.connectin
+  else:
+    drawconnarrow $.child[i] {down} .. tension 4 .. {down} $$.n
+  fi
+enddef;
+vardef internalnodeconnect(text suffixes) =
+  save _fart_, _i_; string _fart_; _i_ = 0;
+  forsuffixes $=suffixes:
+    if unknown _fart_:
+      _fart_ := str $;
+    elseif _i_ <= $.nkeys:
+      forsuffixes $$=scantokens _fart_:
+       internalnodeconnectone($$,$,_i_);
+      endfor
+      _i_ := _i_ + 1;
+    fi
+  endfor
+enddef;
+
+vardef internalnodeit@#(text components) =
+  _elementit.@#(nullpicture, 0, 0, push, false, false);
+  _n_ := str @#;
+  generic_declare(numeric) _n.linkwidth, _n.nkeys;
+  generic_declare(boolean) _n.deleted, _n.isleaf, _n.locked;
+  generic_declare(pair) _n.connectin;
+  _n_ := str @# & ".keypic0";
+  generic_declare(picture) _n;
+  _n_ := str @# & ".child0";
+  generic_declare(pair) _n;
+  @#.isleaf = false;
+  save _i_, _nk_; picture _i_; _nk_ := 0;
+  for _i_ = components:
+    @#.keypic[_nk_] = _i_;
+    @#.child[_nk_] = @#.sw + (@#.linkwidth + _nk_*@#.keywidth,0);
+    _nk_ := _nk_ + 1;
+  endfor
+  @#.child[_nk_] = @#.sw + (@#.linkwidth + _nk_*@#.keywidth,0);
+  elemdraw_@# := "drawnode_";
+  sproc_@# := "sizenode_";
+  @#.nkeys = _nk_;
+  @#.keywidth = (@#.width - 2*@#.linkwidth) / @#.nkeys;
+  expandafter def expandafter clearboxes expandafter =
+    clearboxes clearnode_(@#);
+  enddef
+enddef;
+
+vardef drawterminationarrow(expr p) text rest =
+  draw p withpen connectionpen rest;
+  save endpt, enddir; pair endpt;
+  endpt = point length p of p; enddir = angle(direction length p of p);
+  draw ((-4,0) -- (4,0)) shifted endpt rotated (enddir + 90) withpen connectionpen rest;
+  draw ((-2.25,-1.5) -- (2.25,-1.5)) shifted endpt rotated (enddir + 90) withpen connectionpen rest;
+  draw ((-.5,-3) -- (.5,-3)) shifted endpt rotated (enddir + 90) withpen connectionpen rest;
+enddef;
diff --git a/silo/masstree/doc/patches.mp b/silo/masstree/doc/patches.mp
new file mode 100644 (file)
index 0000000..b2a1af1
--- /dev/null
@@ -0,0 +1,44 @@
+input elements;
+
+def thediamond_(suffix $) =
+  $.e -- $.n -- $.w -- $s -- cycle
+enddef;
+
+vardef sizediamond_(suffix $) =
+  save a_, b_;
+  (a_,b_) = .5*(urcorner pic_$ - llcorner pic_$);
+  if (unknown $dy or unknown $dx) and unknown $squatness: $squatness = 1; fi
+  if unknown $dy: $dy = $squatness * ($dx + a_) - b_; fi
+  if unknown $dy:
+    $dy = b_ + circmargin * sqrt($squatness+1) + $squatness * a_;
+  fi
+  sizeelement_($);
+enddef;
+
+vardef cleardiamond_(suffix $) =
+  _n_ := str $;
+  generic_redeclare(numeric) _n.squatness;
+enddef;
+
+vardef patchit@#(expr s, type) =
+  _elementit.@#(s, 0, 0, push, false, false);
+  if type = 0:
+    pproc_.@# := "thecirc_";
+    % sproc_.@# := "sizecirc_";
+  elseif type = 2:
+    _n_ := str @#;
+    generic_declare(numeric) _n.squatness, _n.dx, _n.dy;
+    pproc_.@# := "thediamond_";
+    sproc_.@# := "sizediamond_";
+    expandafter def expandafter clearboxes expandafter =
+      clearboxes cleardiamond_(@#);
+    enddef
+  elseif type = 3:
+    pproc_.@# := "rboxpath_";
+  fi;
+enddef;
+
+vardef drawconnpatch(suffix $,$$)(text conn) text rest =
+  interim linejoin := mitered;
+  drawarrow ($.c conn $$.c) cutbefore bpath.$ cutafter bpath.$$ withpen connectionpen rest
+enddef;
diff --git a/silo/masstree/doc/remove1.mp b/silo/masstree/doc/remove1.mp
new file mode 100644 (file)
index 0000000..48568b9
--- /dev/null
@@ -0,0 +1,327 @@
+input masstree;
+verbatimtex %&latex
+\documentclass[12pt]{article}
+\usepackage{elemfig,amsmath}
+\begin{document}
+\newcommand{\xlab}[1]{\mlabel{\textsc{#1}}}
+etex;
+
+picture data[];
+data[0] = btex \xlab{a} etex; data[1] = btex \xlab{b} etex; data[2] = btex \xlab{c} etex;
+data[3] = btex \xlab{d} etex; data[4] = btex \xlab{e} etex; data[5] = btex \xlab{f} etex;
+data[6] = btex \xlab{g} etex; data[7] = btex \xlab{h} etex; data[8] = btex \xlab{i} etex;
+data[9] = btex \xlab{j} etex; data[10] = btex \xlab{k} etex; data[11] = btex \xlab{l} etex;
+data[12] = btex \xlab{m} etex; data[13] = btex \xlab{n} etex; data[14] = btex \xlab{o} etex;
+
+pair min_reasonable_cell; min_reasonable_cell := (0,0);
+for _i_ = 0 upto 14:
+  min_reasonable_cell :=
+    (max(xpart min_reasonable_cell, xpart (urcorner data[_i_] - llcorner data[_i_])),
+      max(ypart min_reasonable_cell, ypart (urcorner data[_i_] - llcorner data[_i_])));
+endfor;
+color deletedelement; deletedelement = .5white;
+
+picture emptyqueue; emptyqueue := btex \phantom{\elementlabel{Queue}} etex;
+picture vemptyqueue; vemptyqueue := emptyqueue rotated 90 xscaled .4;
+hardborderscale = 3;
+
+beginfig(1);
+  % initial tree
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  leafconnect(a,d,g,j,m);
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(2);
+  % delete element E
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  d.locked = true;
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  fill nodecellbpath(d,1) withcolor deletedelement;
+  leafconnect(a,d,g,j,m);
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(3);
+  % delete node DEF: poison next pointer
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  d.locked = d.deleted = true;
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  fill nodecellsbpath(d,0,2) withcolor deletedelement;
+  fill leafnextbpath(d) withcolor black;
+  leafconnect(a,d,g,j,m);
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(31);
+  % delete node DEF: poison ABC's next pointer
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  d.locked = d.deleted = true;
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  fill nodecellsbpath(d,0,2) withcolor deletedelement;
+  fill leafnextbpath(d) withcolor black;
+  fill leafnextbpath(a) withcolor black;
+  leafconnect(a,d,g,j,m);
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(4);
+  % delete node DEF: adjust GHI.prev
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  d.locked = d.deleted = true;
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  fill nodecellsbpath(d,0,2) withcolor deletedelement;
+  fill leafnextbpath(d) withcolor black;
+  fill leafnextbpath(a) withcolor black;
+  leafconnect(a,d);
+  leafconnect(g,j,m);
+  g.prevpath = g.prev .. (d.se - (0,3)) .. tension 1.5 .. (d.sw - (0,3)) .. (a.previn - (0,1));
+  d.nextpath = d.next -- g.nextin;
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(5);
+  % delete node DEF: adjust ABC.next
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  d.locked = d.deleted = true;
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  fill nodecellsbpath(d,0,2) withcolor deletedelement;
+  fill leafnextbpath(d) withcolor black;
+  leafconnect(g,j,m);
+  a.nextpath = a.next .. (d.nw + (0,3)) .. tension 1.5 .. (d.ne + (0,3)) .. (g.nextin + (0,1));
+  d.prevpath = d.prev -- a.previn;
+  d.nextpath = d.next -- g.nextin;
+  g.prevpath = g.prev .. (d.se - (0,3)) .. tension 1.5 .. (d.sw - (0,3)) .. (a.previn - (0,1));
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(6);
+  % delete node DEF: adjust internal node
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  d.deleted = true;
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[6], nullpicture, nullpicture);
+  ina.locked = true;
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  fill nodecellsbpath(d,0,2) withcolor deletedelement;
+  fill leafnextbpath(d) withcolor black;
+  leafconnect(g,j,m);
+  a.nextpath = a.next .. (d.nw + (0,3)) .. tension 1.5 .. (d.ne + (0,3)) .. (g.nextin + (0,1));
+  d.prevpath = d.prev -- a.previn;
+  d.nextpath = d.next -- g.nextin;
+  g.prevpath = g.prev .. (d.se - (0,3)) .. tension 1.5 .. (d.sw - (0,3)) .. (a.previn - (0,1));
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(7);
+  % delete node GHI: after leaf unlink
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  d.deleted = true;
+  leafit.g(data[6], data[7], data[8]);
+  g.locked = g.deleted = true;
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[6], nullpicture, nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  fill nodecellsbpath(g,0,2) withcolor deletedelement;
+  fill leafnextbpath(g) withcolor black;
+  leafconnect(j,m);
+  a.nextpath = a.next .. (g.nw + (0,3)) .. tension 1.5 .. (g.ne + (0,3)) .. (j.nextin + (0,1));
+  g.prevpath = g.prev -- a.previn;
+  g.nextpath = g.next -- j.nextin;
+  j.prevpath = j.prev .. (g.se - (0,3)) .. tension 1.5 .. (g.sw - (0,3)) .. (a.previn - (0,2));
+  drawelement(a,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(8);
+  % delete node GHI: after internal node
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  d.deleted = true;
+  leafit.g(data[6], data[7], data[8]);
+  g.deleted = true;
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(nullpicture, nullpicture, nullpicture);
+  ina.locked = true;
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  fill nodecellsbpath(g,0,2) withcolor deletedelement;
+  fill leafnextbpath(g) withcolor black;
+  leafconnect(j,m);
+  a.nextpath = a.next .. (g.nw + (0,3)) .. tension 1.5 .. (g.ne + (0,3)) .. (j.nextin + (0,1));
+  g.prevpath = g.prev -- a.previn;
+  g.nextpath = g.next -- j.nextin;
+  j.prevpath = j.prev .. (g.se - (0,3)) .. tension 1.5 .. (g.sw - (0,3)) .. (a.previn - (0,2));
+  drawelement(a,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(9);
+  % delete node GHI: after collapse
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  d.deleted = true;
+  leafit.g(data[6], data[7], data[8]);
+  g.deleted = true;
+  leafit.j(data[9], data[10], data[11]);
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(nullpicture, nullpicture, nullpicture);
+  ina.deleted = true;
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  root.locked = true;
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  fill nodecellsbpath(g,0,2) withcolor deletedelement;
+  fill leafnextbpath(g) withcolor black;
+  leafconnect(j,m);
+  a.nextpath = a.next .. (g.nw + (0,3)) .. tension 1.5 .. (g.ne + (0,3)) .. (j.nextin + (0,1));
+  g.prevpath = g.prev -- a.previn;
+  g.nextpath = g.next -- j.nextin;
+  j.prevpath = j.prev .. (g.se - (0,3)) .. tension 1.5 .. (g.sw - (0,3)) .. (a.previn - (0,2));
+  drawelement(a,g,j,m,ina,inj,root);
+  internalnodeconnect(inj,j,m);
+  drawconnarrow ina.child[0] {down} .. tension 4 .. {down} (a.n + (3,0));
+  drawconnarrow root.child[0] {down} .. tension 2 and 1 .. (ina.nw + (0,4)) .. {down} a.n;
+  internalnodeconnectone(root,inj,1);
+endfig;
+
+end
diff --git a/silo/masstree/doc/remove2.mp b/silo/masstree/doc/remove2.mp
new file mode 100644 (file)
index 0000000..1f9db91
--- /dev/null
@@ -0,0 +1,408 @@
+input masstree;
+verbatimtex %&latex
+\documentclass[12pt]{article}
+\usepackage{elemfig,amsmath}
+\begin{document}
+\newcommand{\xlab}[1]{\mlabel{\textsc{#1}}}
+etex;
+
+picture data[];
+data[0] = btex \xlab{a} etex; data[1] = btex \xlab{b} etex; data[2] = btex \xlab{c} etex;
+data[3] = btex \xlab{d} etex; data[4] = btex \xlab{e} etex; data[5] = btex \xlab{f} etex;
+data[6] = btex \xlab{g} etex; data[7] = btex \xlab{h} etex; data[8] = btex \xlab{i} etex;
+data[9] = btex \xlab{j} etex; data[10] = btex \xlab{k} etex; data[11] = btex \xlab{l} etex;
+data[12] = btex \xlab{m} etex; data[13] = btex \xlab{n} etex; data[14] = btex \xlab{o} etex;
+picture dotdata;
+dotdata = btex \xlab{\dots\dots} etex;
+
+pair min_reasonable_cell; min_reasonable_cell := (0,0);
+for _i_ = 0 upto 14:
+  min_reasonable_cell :=
+    (max(xpart min_reasonable_cell, xpart (urcorner data[_i_] - llcorner data[_i_])),
+      max(ypart min_reasonable_cell, ypart (urcorner data[_i_] - llcorner data[_i_])));
+endfor;
+color deletedelement; deletedelement = .5white;
+color upperlayer, upperlayerfill; upperlayer = (0,0,.5); upperlayerfill = (.9,.9,1);
+
+picture emptyqueue; emptyqueue := btex \phantom{\elementlabel{Queue}} etex;
+picture vemptyqueue; vemptyqueue := emptyqueue rotated 90 xscaled .4;
+hardborderscale = 3;
+
+beginfig(1);
+  % tree with JKL removed
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  j.locked = j.deleted = true;
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  leafconnect(a,d,g);
+  fill nodecellsbpath(j,0,2) withcolor deletedelement;
+  fill leafnextbpath(j) withcolor black;
+  g.nextpath = g.next .. (j.nw + (0,3)) .. tension 1.5 .. (j.ne + (0,3)) .. (m.nextin + (0,1));
+  j.prevpath = j.prev -- g.previn;
+  j.nextpath = j.next -- m.nextin;
+  m.prevpath = m.prev .. (j.se - (0,3)) .. tension 1.5 .. (j.sw - (0,3)) .. (g.previn - (0,1));
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,j,m);
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(2);
+  % bad idea: tree with M in internal node removed
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  j.deleted = true;
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(nullpicture, nullpicture, nullpicture);
+  inj.locked = true;
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  leafconnect(a,d,g);
+  fill nodecellsbpath(j,0,2) withcolor deletedelement;
+  fill leafnextbpath(j) withcolor black;
+  g.nextpath = g.next .. (j.nw + (0,3)) .. tension 1.5 .. (j.ne + (0,3)) .. (m.nextin + (0,1));
+  j.prevpath = j.prev -- g.previn;
+  j.nextpath = j.next -- m.nextin;
+  m.prevpath = m.prev .. (j.se - (0,3)) .. tension 1.5 .. (j.sw - (0,3)) .. (g.previn - (0,1));
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(inj,m);
+  internalnodeconnect(root,ina,inj);
+  label.rt(btex \textsf{\textbf{~~XXX}} etex, inj.e);
+endfig;
+
+beginfig(3);
+  % tree with M stubbed out
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  j.deleted = true;
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  inj.locked = true;
+  internalnodeit.root(data[9], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  leafconnect(a,d,g);
+  fill nodecellsbpath(j,0,2) withcolor deletedelement;
+  fill leafnextbpath(j) withcolor black;
+  g.nextpath = g.next .. (j.nw + (0,3)) .. tension 1.5 .. (j.ne + (0,3)) .. (m.nextin + (0,1));
+  j.prevpath = j.prev -- g.previn;
+  j.nextpath = j.next -- m.nextin;
+  m.prevpath = m.prev .. (j.se - (0,3)) .. tension 1.5 .. (j.sw - (0,3)) .. (g.previn - (0,1));
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnectone(inj,m,1);
+  drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(4);
+  % tree with M replacing J at the root
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  j.deleted = true;
+  leafit.m(data[12], data[13], data[14]);
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[12], nullpicture, nullpicture);
+  root.locked = true;
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  leafconnect(a,d,g);
+  fill nodecellsbpath(j,0,2) withcolor deletedelement;
+  fill leafnextbpath(j) withcolor black;
+  g.nextpath = g.next .. (j.nw + (0,3)) .. tension 1.5 .. (j.ne + (0,3)) .. (m.nextin + (0,1));
+  j.prevpath = j.prev -- g.previn;
+  j.nextpath = j.next -- m.nextin;
+  m.prevpath = m.prev .. (j.se - (0,3)) .. tension 1.5 .. (j.sw - (0,3)) .. (g.previn - (0,1));
+  drawelement(a,d,g,j,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnectone(inj,m,1);
+  drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(5);
+  % tree with MNO removed
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  j.deleted = true;
+  leafit.m(data[12], data[13], data[14]);
+  m.locked = m.deleted = true;
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(data[12], nullpicture, nullpicture);
+  internalnodeit.root(data[12], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  leafconnect(a,d,g);
+  fill nodecellsbpath(m,0,2) withcolor deletedelement;
+  fill leafnextbpath(m) withcolor black;
+  m.prevpath = m.prev -- g.previn;
+  drawelement(a,d,g,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnectone(inj,m,1);
+  drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(6);
+  % tree with MNO removed and internal node removed
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  j.deleted = true;
+  leafit.m(data[12], data[13], data[14]);
+  m.deleted = true;
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(nullpicture, nullpicture, nullpicture);
+  inj.locked = inj.deleted = true;
+  internalnodeit.root(data[12], nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  leafconnect(a,d,g);
+  fill nodecellsbpath(m,0,2) withcolor deletedelement;
+  fill leafnextbpath(m) withcolor black;
+  m.prevpath = m.prev -- g.previn;
+  drawelement(a,d,g,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
+  internalnodeconnect(root,ina,inj);
+endfig;
+
+beginfig(7);
+  % tree with MNO removed and internal node removed
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  leafit.j(data[9], data[10], data[11]);
+  j.deleted = true;
+  leafit.m(data[12], data[13], data[14]);
+  m.deleted = true;
+  boxjoin();
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.inj(nullpicture, nullpicture, nullpicture);
+  inj.deleted = true;
+  internalnodeit.root(nullpicture, nullpicture, nullpicture);
+  root.locked = true;
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  inj.s = .5[j.nw,m.ne] + (0,24);
+  root.s = .5[ina.nw,inj.ne] + (0,24);
+  fixelement(a,d,g,j,m);
+  fixelement(root,ina,inj);
+  leafconnect(a,d,g);
+  fill nodecellsbpath(m,0,2) withcolor deletedelement;
+  fill leafnextbpath(m) withcolor black;
+  m.prevpath = m.prev -- g.previn;
+  drawelement(a,d,g,m,ina,inj,root);
+  internalnodeconnect(ina,a,d,g);
+  drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
+  internalnodeconnect(root,ina);
+endfig;
+
+beginfig(8);
+  % tree with MNO removed, gclayer begin
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.nextpath = x.prevpath = emptypath;
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.root(nullpicture, nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  root.sw = ina.n + (0,24);
+  .8[x.sw,x.se] = root.n + (0,24);
+  fixelement(a,d,g);
+  fixelement(root,ina,x);
+  leafconnect(a,d,g);
+  drawelement(a,d,g,ina,root);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} root.n withpen connectionpen scaled 2 withcolor upperlayer;
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(root,ina);
+endfig;
+
+beginfig(9);
+  % tree with MNO removed, gclayer begin
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.locked = true;
+  x.nextpath = x.prevpath = emptypath;
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.root(nullpicture, nullpicture, nullpicture);
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  root.sw = ina.n + (0,24);
+  .8[x.sw,x.se] = root.n + (0,24);
+  fixelement(a,d,g);
+  fixelement(root,ina,x);
+  leafconnect(a,d,g);
+  drawelement(a,d,g,ina,root);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} root.n withpen connectionpen scaled 2 withcolor upperlayer;
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(root,ina);
+endfig;
+
+beginfig(11);
+  % tree with MNO removed, gclayer begin
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.locked = true;
+  x.nextpath = x.prevpath = emptypath;
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.root(nullpicture, nullpicture, nullpicture);
+  root.locked = true;
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  root.sw = ina.n + (0,24);
+  .8[x.sw,x.se] = root.n + (0,24);
+  fixelement(a,d,g);
+  fixelement(root,ina,x);
+  leafconnect(a,d,g);
+  drawelement(a,d,g,ina,root);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} root.n withpen connectionpen scaled 2 withcolor upperlayer;
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(root,ina);
+endfig;
+
+beginfig(12);
+  % tree with MNO removed, gclayer begin
+  boxjoin(b.w - a.e = (20,0));
+  leafit.a(data[0], data[1], data[2]);
+  leafit.d(data[3], data[4], data[5]);
+  leafit.g(data[6], data[7], data[8]);
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.locked = true;
+  x.nextpath = x.prevpath = emptypath;
+  internalnodeit.ina(data[3], data[6], nullpicture);
+  internalnodeit.root(nullpicture, nullpicture, nullpicture);
+  root.locked = true;
+  ina.s = .5[a.nw,g.ne] + (0,24);
+  root.sw = ina.n + (0,24);
+  .8[x.sw,x.se] = root.n + (0,24);
+  fixelement(a,d,g);
+  fixelement(root,ina,x);
+  leafconnect(a,d,g);
+  drawelement(a,d,g,ina,root);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} (ina.n - (4,0)) withpen connectionpen scaled 2 withcolor upperlayer;
+  internalnodeconnect(ina,a,d,g);
+  internalnodeconnect(root,ina);
+endfig;
+
+beginfig(15);
+  % tree with MNO removed, gclayer begin
+  leafit.a(nullpicture, nullpicture, nullpicture);
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.locked = true;
+  x.nextpath = x.prevpath = emptypath;
+  .5[x.sw,x.se] = a.n + (0,24);
+  fixelement(a,x);
+  leafconnect(a);
+  drawelement(a,x);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} a.n withpen connectionpen scaled 2 withcolor upperlayer;
+endfig;
+
+beginfig(16);
+  % tree with MNO removed, gclayer begin
+  leafit.a(nullpicture, nullpicture, nullpicture);
+  a.locked = true;
+  boxjoin();
+  leafit.x(dotdata, dotdata, dotdata, dotdata);
+  x.locked = true;
+  x.nextpath = x.prevpath = emptypath;
+  .5[x.sw,x.se] = a.n + (0,24);
+  fixelement(a,x);
+  leafconnect(a);
+  drawelement(a,x);
+  fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
+  begingroup
+    interim linecap := butt;
+    draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
+  endgroup;
+  drawconnarrow x.value[1] {down} .. tension 2 .. {down} a.n withpen connectionpen scaled 2 withcolor upperlayer;
+endfig;
+
+end
diff --git a/silo/masstree/doc/spec.tex b/silo/masstree/doc/spec.tex
new file mode 100644 (file)
index 0000000..f367fa4
--- /dev/null
@@ -0,0 +1,1395 @@
+% -*- tex-command: "pdflatex" -*-
+\documentclass[11pt]{article}
+\usepackage{times,mathptmx,amsmath,amssymb,graphicx,float}
+\usepackage[tmargin=1in,hmargin=.75in,height=9in,columnsep=2pc,letterpaper]{geometry}
+\usepackage[T1]{fontenc}
+\makeatletter
+\newcommand{\Keyset}{\ensuremath{\mathcal K}}
+\newcommand{\Valueset}{\ensuremath{\mathcal V}}
+\newcommand{\None}{\textsc{None}}
+\newcommand{\NONE}{\textsc{none}}
+\newcommand{\FALSE}{\textsc{false}}
+\newcommand{\TRUE}{\textsc{true}}
+\newcommand{\Bool}{\textsc{Bool}}
+\newcommand{\Nat}{\ensuremath{\mathbb N}}
+\newcommand{\sysGet}{\textit{Get}}
+\newcommand{\sysget}{\textit{get}}
+\newcommand{\sysgets}{\textit{get\/}s}
+\newcommand{\sysPut}{\textit{Put}}
+\newcommand{\sysput}{\textit{put}}
+\newcommand{\sysputs}{\textit{put\/}s}
+\newcommand{\sysScan}{\textit{Scan}}
+\newcommand{\sysscan}{\textit{scan}}
+\newcommand{\Inv}[1]{\ensuremath{\textrm{inv}(#1)}}
+\newcommand{\Res}[1]{\ensuremath{\textrm{res}(#1)}}
+\newcommand{\Precedes}{<}
+\newcommand{\NotPrecedes}{\not <}
+\newcommand{\Overlaps}{\approx}
+\newcommand{\PrecedesK}[1]{\ensuremath{\mathrel{<_{#1}}}}
+\newcommand{\PrecedesEqK}[1]{\ensuremath{\mathrel{\leq_{#1}}}}
+\newcommand{\V}[1]{\textit{#1}}
+\newcommand{\N}[1]{\text{\upshape{#1}}}
+\newcommand{\NIL}{\textsc{nil}}
+\newcommand{\NOTFOUND}{\textsc{notfound}}
+\newcommand{\VALUE}{\textsc{value}}
+\newcommand{\LAYER}{\textsc{layer}}
+\newcommand{\MARK}{\textsc{mark}}
+\newcommand{\INSERTING}{\textsc{inserting}}
+\newcommand{\REMOVING}{\textsc{removing}}
+\newcommand{\readnow}[1]{\ensuremath{{}^\circ#1}}
+\newcommand{\fencebase}{\text{- - - - - - - - -}}
+\newcommand{\rcufencebase}{\text{* * * * * * * * *}}
+\newcommand{\fence}{\ensuremath{\fencebase}}
+%\newcommand{\acquirefence}{\ensuremath{\fencebase_{\textsc{acquire}}}}
+%\newcommand{\releasefence}{\ensuremath{\fencebase_{\textsc{release}}}}
+\let\acquirefence\fence
+\let\releasefence\fence
+\newcommand{\rcufence}{\ensuremath{\rcufencebase_{\textsc{rcu
+        fence}}}}
+\newcommand{\substring}[3]{\ensuremath{#1[#2\mathbin:#3]}}
+\newcommand{\Returns}[1]{\ensuremath{\rightarrow\text{#1}}}
+\newcommand{\Bplus}{B\(^+\)}
+\newcommand{\Blink}{B\(^{\text{link}}\)}
+\newcommand{\LEAFWIDTH}{\ensuremath{W_\text{leaf}}}
+\newcommand{\INTERIORWIDTH}{\ensuremath{W_\text{interior}}}
+\newcommand{\CX}[1]{\small{\textit{// #1}}}
+\newcommand{\C}[1]{\unskip\qquad\CX{#1}}
+\newcommand{\ITEM}[1]{\textsc{#1}}
+\newcommand{\AT}[2]{\ensuremath{#1_{@#2}}}
+\newenvironment{programtabbing}%
+  {\begingroup\parskip\z@\begin{tabbing}\quad\qquad~~\= \quad~~\= \quad~~\= \quad~~\=\kill}%
+  {\unskip\end{tabbing}\endgroup}
+\gdef\programtabskip{\leavevmode\raise1.5ex\hbox{\strut}}
+\gdef\_{\char`\_}
+\frenchspacing
+\makeatother
+\begin{document}
+
+\section{Definitions}
+
+A Masstree maps \emph{keys} to \emph{values}. Keys are variable-length strings. Values are arbitrary.
+Let \Keyset\ be the set of keys and \Valueset\ the set of values.
+
+Masstree's operations are \sysput, \sysget, and \sysscan.
+
+\begin{itemize}
+\item \(\sysput : \Keyset \times \Valueset \mapsto \None\)
+\item \(\sysget : \Keyset \mapsto (\Valueset \cup \None)\)
+\item \(\sysscan : \Keyset \times (\Keyset \times \Valueset \mapsto \Bool) \mapsto \None\)
+\end{itemize}
+
+\noindent%
+\sysPut\
+stores a value at a given key. \sysGet\ returns the value for a given
+key (or \NONE\ if there is no value). \sysScan\ fetches multiple keys
+and values. It takes an initial key $k_0$ and a function object $f$.
+The function object is called for every key $k \geq k_0$ in the
+Masstree, once per key, in increasing order by key. Its arguments are
+a key and the corresponding value. We expect the scan function will
+save copies of the keys and values in the object, or count them, or
+whatever is appropriate, and that scan's caller will extract those
+values once the scan returns. The scan stops when the Masstree
+runs out of values or the function object returns false, whichever
+happens first.
+
+Masstree can support other operations, such as remove and a compare-and-swap-style atomic put, but these three suffice to discuss correctness.
+
+
+\section{Correctness}
+
+Masstree is a concurrent data structure. This means that multiple threads access Masstree simultaneously, complicating notions of correctness. Roughly speaking, Masstree is correct if every \sysget\ returns the most recent \sysput\ for that key, but when \sysgets\ and \sysputs\ overlap this gets a little complicated.
+
+Multiple \emph{threads} submit \emph{invocations} to Masstree and
+receive \emph{responses}. An example invocation is \(\sysput(k,v)\): put
+value $v$ at key $k$. The pair of an invocation and its response is
+called an \emph{action}. An invocation or response is
+called a \emph{half-action}.
+
+We assume a \sysget\ or \sysput\ response contains \emph{the
+relevant \sysput\ action}. For \sysget, this is the \sysput\ action that
+stored the corresponding value (or \NONE\ if there was no previous
+\sysput\ for that key). For \sysput, this is the immediately preceding
+\sysput\ action for that key. This differs from the methods' normal
+signatures, but simplifies the model. The response to a \sysscan\ action
+is a \emph{set} of \sysput\ actions. Here's an example sequence of
+actions:
+
+\begin{flushleft}
+\begin{tabular}{@{}rll}
+& Invocation & Response \\
+$a_1$ & $\sysget(k)$ & \NONE \\
+$a_2$ & $\sysput(k, v_2)$ & \NONE \\
+$a_3$ & $\sysput(k, v_3)$ & $a_2$ \\
+$a_4$ & $\sysget(k)$ & $a_3$ \\
+$a_5$ & $\sysput(k+1, v_5)$ & \NONE \\
+$a_6$ & $\sysscan(k, \dots)$ & $\{a_3,a_5\}$ \\
+\end{tabular}
+\end{flushleft}
+
+We assume that all half-actions are sequenced into a total order, which
+we might call global time. In this time order, every invocation precedes
+its corresponding response---$\Inv{a} < \Res{a}$ for all
+$a$---though other half-actions may intervene between them.
+
+The total order on half-actions imposes a \emph{partial} order on
+actions. Given actions $a_1$ and $a_2$, we say that $a_1$
+\emph{precedes} $a_2$, written $a_1 \Precedes a_2$, if $a_1$'s
+response is before $a_2$'s invocation in the global time order:
+$\Res{a_1} < \Inv{a_2}$. We say that $a_1$ \emph{overlaps} $a_2$,
+written $a_1 \Overlaps a_2$, if $a_1 \NotPrecedes a_2$ and $a_2
+\NotPrecedes a_1$.
+
+Masstree is correct if there exist per-key total orders
+\(\PrecedesK{k}\), where \(\PrecedesK{k}\) for key $k$ contains the set
+of \sysput\ actions on $k$, so that the following properties are
+satisfied.
+
+\begin{enumerate}
+\item Every \sysput\ action on $k$ is in \(\PrecedesK{k}\).
+\item The \(\PrecedesK{k}\) order agrees with the partial order on actions. That is, if $a_1 \Precedes a_2$ and $a_1$ and $a_2$ are \sysput\ actions on $k$, then $a_1 \PrecedesK{k} a_2$.
+\item The \(\PrecedesK{k}\) order agrees with \sysput\ responses. That is, assuming $\mathord{\PrecedesK{k}} = [a_0, a_1, a_2, \dots]$, then $\Res{a_0} = \NONE$, $\Res{a_1} = a_0$, $\Res{a_2} = a_1$, and so forth.
+\item Given \(a\), a \sysget\ action on $k$ with response \NONE, no
+  \(\sysput(k, \cdot)\) actions precede \(a\) in the partial order on actions.
+\item Given \(a\), a \sysget\ action on $k$ with response $b \neq \NONE$, $b$ is a \sysput\ on $k$ that either overlaps with $a$ or immediately precedes $a$ in $\PrecedesK{k}$. That is, $\Res{a} \NotPrecedes \Inv{b}$, and there is no $x$ with $b \PrecedesK{k} x \Precedes a$.
+\item Given \(a_1 \Precedes a_2\), two \sysget\ actions on the same $k$ that originate \emph{from the same thread}, we must have \(\Res{a_1} \PrecedesEqK{k} \Res{a_2}\). That is, no thread sees ``time'' go backwards.
+\item Given \(a\), a \sysscan\ action starting from $k$, a corresponding set of \sysget\ operations, returning the same results as the \sysscan, would also follow these rules. (As an example, if \(b = \sysput(k, \cdot) \in \Res{a}\), then $b$ must be a \sysput\ on $k$ that either overlaps with $a$ or immediately precedes $a$ in $\PrecedesK{k}$. A key $x$ in the scan range with no corresponding response is like a \sysget\ action that returned \NONE: no \(\sysput(x, \cdot)\) actions can precede \(a\).)
+\end{enumerate}
+
+
+\section{Data structures}
+
+Here's a picture of a Masstree layer.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{examples_1}
+\caption{Example Masstree layer.}
+\end{figure}
+
+\noindent
+Leaves are on the bottom row, interior nodes above. The letters are
+keys.
+
+Leaves are connected in a doubly-linked list (dotted arrows); this
+facilitates scans and reverse scans and speeds up some other operations.
+(See \Blink\ trees.) A leaf \(n\) contains \(n.\V{size}\) keys and
+values, where \(0 \leq n.\V{size} \leq \LEAFWIDTH = 15\).
+
+A leaf \(n\) is responsible for all keys \(k\) in the range
+\(\N{lowkey}(n) \leq k < \N{highkey}(n)\). Each leaf stores its lowkey
+explicitly, and that lowkey \emph{never changes}. The highkey is not
+stored in the leaf---it equals the \emph{next} leaf's lowkey---and can
+change over time. If leaf \(n\) splits, then \(\N{highkey}(n)\)
+decreases; if the leaf following \(n\) is deleted, then
+\(\N{highkey}(n)\) increases to cover the deleted leaf's key range. The
+layer's leftmost leaf has lowkey \(-\infty\).
+
+Interior nodes form a B$^+$ tree for fast access to data stored in
+leaves. Interior nodes are \emph{not} linked
+together.\footnote{\Blink\ trees also connect interior nodes in linked
+lists, but in our tests this didn't speed anything up, and it would
+complicate remove.}
+
+An interior node \(n\) contains \(n.\V{size}\) keys, where \(0 \leq
+n.\V{size} \leq \INTERIORWIDTH = 15\). A node with \(n.\V{size}\) keys
+contains exactly \(n.\V{size} + 1\) child pointers. Keys are stored in a
+\(n.\V{kslice}\) array (see \S\ref{s:keys} for more on key slices).
+Values for key \(k\) are found in one of the underlying children:
+%
+\[
+\text{The child containing \(k\)} =
+\begin{cases}
+n.\V{child}[0] & \text{if \(k < n.\V{kslice}[0]\),} \\
+n.\V{child}[1] & \text{if \(n.\V{kslice}[0] \leq k < n.\V{kslice}[1]\),} \\
+\vdots \\
+n.\V{child}[n.\V{size}] & \text{if \(k \geq n.\V{kslice}[n.\V{size}-1]\).}
+\end{cases}
+\]
+%
+If \(n.\V{size} = 0\), then all values are found in \(n.\V{child}[0]\).
+
+Although each interior node is responsible for a range of keys, this
+range (unlike with leaves) is \emph{implicit}, depending on keys higher
+in the tree. For example, in the figure, interior node (1) is
+responsible for the key range \([\textsc{j}, \infty)\), but bounds
+\textsc{j} and \(\infty\) are implicit from context: if the root node
+contained different keys, (1) might be responsible for
+\([\textsc{e},\textsc{q})\) or some other range. This means that we
+can't always tell from local information whether a key belongs in an
+interior node. This has consequences for the remove operation, as we
+will see.
+
+
+\section{Keys}
+\label{s:keys}
+
+Masstree \emph{keys} are variable-length strings ordered
+lexicographically.
+%
+Masstree divides these strings into eight-byte \emph{slices}; the
+B-tree structures that make up a Masstree store these slices as keys.
+Slice comparison produces the same result as lexicographic comparison
+of the underlying keys.
+
+Masstree code uses objects of type ``key.''
+%
+A key is basically a string decomposed into three parts, a prefix, a
+slice, and a suffix.
+%
+The \N{shift\_key} function, which is called when walking from one
+layer of Masstree to the next, shifts the current slice into the
+prefix, and shifts the next eight bytes of the suffix into the slice.
+
+If \(s\) is a string, let \(\substring{s}{a}{b}\) represent the
+substring of \(s\) starting at index \(a\) and ending just before
+index \(b\).
+%
+Thus, \(\substring{s}{0}{s.\V{length}} = s\), and
+\(\substring{s}{i}{i}\) is empty for any \(i\).
+%
+The substring operation constrains \(a\) and \(b\) to the appropriate
+bounds:
+%
+\[ \substring{s}{a}{b} =
+\substring{s}{\max(a,0)}{\min(b,s.\V{length})}. \]
+
+Then the key type's methods are as follows.
+
+\begin{flushleft}
+\begin{tabular}{@{}p{.5\hsize}@{}p{.5\hsize}@{}}
+\begin{programtabbing}
+\textbf{class key}: \\
+\> representation: string \(s\), int \(p\) \\
+\programtabskip
+\> constructor(string \(s\)): \\
+\>\> return \(\N{key}\{s = s, p = 0\}\) \\
+\programtabskip
+\> slice(key \(k\)) \Returns{string}: \\
+\>\> return \(\substring{k.s}{k.p}{k.p+8}\) \\
+\programtabskip
+\> suffix(key \(k\)) \Returns{string}: \\
+\>\> return \(\substring{k.s}{k.p+8}{k.s.\V{length}}\) \\
+\programtabskip
+\> length(key \(k\)) \Returns{int}: \\
+\>\> return \(k.s.\V{length} - k.p\)
+\end{programtabbing}
+&
+\begin{programtabbing}
+\\
+\\
+\programtabskip
+\> prefix(key \(k\)) \Returns{string}: \\
+\>\> return \(\substring{k.s}{0}{k.p}\) \\
+\programtabskip
+\> shift\_key(key \(k\)) \Returns{key}: \\
+\>\> // precondition: \(k.\V{length} > 8\) \\
+\>\> return \(\N{key}\{s = k.s, p = k.p + 8\}\) \\
+\programtabskip
+\> reset\_key(key \(k\)) \Returns{key}: \\
+\>\> // undoes all prior shift\_keys \\
+\>\> return \(\N{key}\{s = k.s, p = 0\}\)
+\end{programtabbing}
+\end{tabular}
+\end{flushleft}
+
+Masstree leaves store key slices, key lengths, and key suffixes. These
+are stored in separate arrays: the key at position \(\pi\) has slice
+\(n.\V{kslice}[\pi]\), length \(n.\V{klength}[\pi]\), and suffix
+\(n.\V{ksuffix}[\pi]\). (It is important to store slices and lengths
+inline, in the node, to allow prefetching. Suffixes, which can have
+arbitrary length, must be stored out of line.) Multiple keys with the
+same slice and \emph{different} suffixes are stored in a new layer.
+
+Interior nodes, unlike leaves, store \emph{only} key slices. (This
+speeds up the code, simplifies suffix memory management, and reduces
+memory usage.) As a consequence, all keys with slice \(k\) \emph{must}
+be stored in the same leaf. This choice constrains split.
+
+
+\section{Memory model}
+
+We write memory fences as rows of dashes\ \fence; on x86 (TSO) machines,
+these fences only affect the compiler's code generation---no machine
+instructions are required.
+
+We assume that, except as constrained by fences, the compiler may
+arbitrarily cache and reorder reads from and writes to node data. For
+example, the following code might read \(n.\V{version}\) once or twice,
+and might write \(n.\V{kslice}[0]\) once or twice:
+
+\begin{programtabbing}
+\(a \gets n.\V{version}\) \\
+\(++n.\V{kslice}[0]\) \\
+\(b \gets n.\V{version}\) \\
+\(n.\V{kslice}[0] \gets 0\)
+\end{programtabbing}
+
+\noindent
+However, this code \emph{must} read \(n.\V{version}\) twice, and write
+\(n.\V{kslice}[0]\) twice:
+
+\begin{programtabbing}
+\(a \gets n.\V{version}\) \\
+\(++n.\V{kslice}[0]\) \\
+\fence \\
+\(b \gets n.\V{version}\) \\
+\(n.\V{kslice}[0] \gets 0\)
+\end{programtabbing}
+
+We occasionally use a superscript O notation, as in
+\(\readnow{n.\V{version}}\), to indicate that a value is
+read from main memory at that point (i.e., no previously cached
+value allowed). Some unmarked reads must also complete from main
+memory, specifically those after fences.
+
+All reads and writes of individual variables are assumed to be atomic.
+We also assume one atomic compare-and-swap operation, cmpxchg, which has
+the following effect (but in one atomic step):
+
+\begin{programtabbing}
+\textbf{cmpxchg}(address \V{value}, value \V{expected}, value
+\V{desired}) \Returns{bool}: \\
+\> if \(\readnow{\V{value}} = \V{expected}\): \\
+\>\> \(\V{value} \gets \V{desired}\);~ return \TRUE \\
+\> else: \\
+\>\> return \FALSE
+\end{programtabbing}
+
+\section{Versions and locking}
+
+Each node has a 32- or 64-bit \emph{version}.\footnote{64-bit versions
+offer greater protection against overflow. A 32-bit version could wrap,
+causing unexpected reader behavior, if a reader blocked mid-computation
+for \(2^{22}\) inserts.} The version is located at the same place in
+leaf and interior nodes---it's the only common aspect of layout between
+node types. Versions can be manipulated with compare-and-swap operations
+and can be assigned atomically. They have the following fields:
+
+\begin{flushleft}
+\begin{tabular}{@{}lll}
+\V{locked} & 1 bit & whether node is locked \\
+\V{inserting} & 1 bit & whether node has been dirtied for insertion \\
+\V{splitting} & 1 bit & whether node has been dirtied for splitting \\
+&& Together, \V{inserting} and \V{splitting} are the \emph{dirty bits}. \\
+\V{vinsert} & 6 or 16 bits & insert version counter \\
+\V{vsplit} & 19 or 33 bits & split version counter \\
+&& \V{vinsert} overflows into \V{vsplit}; \V{vsplit} is a circular
+counter. \\
+\V{deleted} & 1 bit & whether node has been deleted \\
+\V{isroot} & 1 bit & whether node is root of its layer \\
+\V{isleaf} & 1 bit & whether node is a leaf \\
+\end{tabular}
+\end{flushleft}
+
+The dirty bits alert readers of potentially unstable states that require
+retry. Masstree updates generally involve multiple writes that should be
+considered atomic (for example, inserting a new child into an interior
+node, or updating the various parts of a key in a leaf). In some cases
+Masstree can arrange to ``publish'' an update atomically,
+but this is not always possible. Thus,
+before changing a node in a dangerous way, Masstree writers must mark
+the node as dirty by changing its version. Masstree readers execute
+\emph{read transactions} that are \emph{validated} using versions. First
+they snapshot the node's version, then they snapshot the relevant
+portion of its contents, and finally they compare the node's new version
+to its snapshot. If the versions differ in more than the \V{locked}
+bit (that is, if \(v \oplus \readnow{n.\V{version}} > \V{locked}\)),
+then the node changed in some potentially invalidating way during the
+read operation, and the reader must retry the read transaction.
+
+Masstree's lock and unlock functions are as follows. Note that they
+contain fences.
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{lock}(node $n$): \\
+\> $v \gets n.\V{version}$ \\
+\> while $v.\V{locked}$ or $!\text{cmpxchg}(n.\V{version}, v, v + \V{locked})$: \\
+\>\> $v \gets \readnow{n.\V{version}}$ \\
+\> \acquirefence \\
+\programtabskip
+%
+\textbf{unlock}(node $n$): \C{precondition: \(n\) is locked} \\
+\> $v \gets n.\V{version}$ \\
+\> if $v.\V{inserting}$: \\
+\>\> $++v.\V{vinsert}$ \C{may overflow into \V{vsplit}} \\
+\> else if $v.\V{splitting}$: \\
+\>\> $++v.\V{vsplit}$;~ $v.\V{isroot} \gets 0$ \C{the true root has never split} \\
+\> $v.\{\V{locked},\V{inserting},\V{splitting}\} \gets 0$ \\
+\> \releasefence \\
+\> $n.\V{version} \gets v$
+\end{programtabbing}
+\end{figure}
+
+A read transaction must first wait for its node to reach a stable
+state, so read transactions start with \N{stable\_version}, a form of
+``lock'' that doesn't actually involve locking.
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{stable\_version}(node $n$): \\
+\> $v \gets {n.\V{version}}$ \\
+\> while $v.\V{inserting}$ or $v.\V{splitting}$: \\
+\>\> $v \gets \readnow{n.\V{version}}$ \\
+\> \acquirefence \\
+\> return $v$
+\end{programtabbing}
+\end{figure}
+
+Nodes contain \V{parent} pointers for traversing up the tree. The
+\N{locked\_parent} and \N{true\_root} functions traverse these pointers.
+\N{locked\_parent} must retry because a node's parent can spontaneously
+change, even while the node is locked, if its parent interior node
+splits. The \N{true\_root} function traverses parent pointers until it
+finds the root of a tree.
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{locked\_parent}(node $n$): \C{precondition: \(n\) is locked} \\
+retry:~
+\> $p \gets n.\V{parent}$ \\
+\> if $p \neq \NIL$: \\
+\>\> lock($p$) \\
+\>\> if $p \neq \readnow{n.\V{parent}}$: \C{parent changed underneath us} \\
+\>\>\> unlock($p$);~ goto retry \\
+\> return $p$ \\
+\programtabskip
+%
+\textbf{true\_root}(node $n$): \\
+\> \(p \gets n.\V{parent}\) \\
+\> while \(p \neq \NIL\): \\
+\>\> \(n \gets p\);~ \(p \gets n.\V{parent}\) \\
+\> return \(n\)
+\end{programtabbing}
+\end{figure}
+
+
+\section{Invariants}
+
+We write \(\AT{x}{t}\) for the value of \(x\) at time \(t\).
+Times \(t\) and \(t'\) are \emph{RCU-overlapping} if a
+single reader could be active over some interval including both \(t\)
+and \(t'\).
+
+\paragraph{Root reachability}
+
+Assume that at time \(t\), the true root of a layer is node \(n\), and
+\(n\) is unlocked. Let \(x\) be the root of that layer as observed by
+a concurrent reader active at time \(t\). Then either \(x=n\), or
+\(x.\V{isroot} = \FALSE\) and \(n\) is reachable from \(x\) by a chain
+of \V{parent} pointers.
+
+\paragraph{Parent validity}
+
+If \(n\) is reachable, then \(n.\V{parent}\) is either another
+reachable node or \NIL.
+
+\paragraph{Key stability}
+
+Let \(n\) be a reachable leaf, and consider two times \(t<t'\) where
+\(n.\V{version}\) is the same at both times, and not dirty. Consider a
+slot \(\pi\) reachable via \(\AT{n.\V{permutation}}{t}\). Then
+slot \(\pi\) is also reachable via \(\AT{n.\V{permutation}}{t'}\), and
+all key information in slot \(\pi\) is unchanged from the corresponding values at time
+\(t\). (That is, \(\AT{n.\V{kslice}[\pi]}{t} =
+\AT{n.\V{kslice}[\pi]}{t'}\), \(\AT{n.\V{ksuffix}[\pi]}{t} =
+\AT{n.\V{ksuffix}[\pi]}{t'}\),  and \(\AT{n.\V{klength}[\pi]}{t} =
+\AT{n.\V{klength}[\pi]}{t'}\).)
+
+\paragraph{Interior node validity}
+
+Assume that \(n\) is a reachable interior node with
+\(\AT{n.\V{size}}{t} = s\). Let \(t'>t\) be an RCU-overlapping time with
+\(t\), and let \(x = \AT{n.\V{child}[i]}{t'}\) for some \(i\) with \(0
+\leq i \leq s\). Then either \(x = \NIL\), or \(x\) is a pointer to a
+valid (though not necessarily reachable) node.
+
+\paragraph{Cleanliness}
+
+If node \(n\) is dirty at time \(t\), then \(n\) is locked at time \(t\).
+
+\paragraph{Locking discipline}
+
+If a Masstree interface function (such as get, put, or remove) locks a
+node, then it unlocks that node before returning to the user.
+
+Node locks are obtained sequentially \emph{down} from layer to layer, but
+within a layer, they are obtained \emph{up} from leaves toward the root.
+
+\paragraph{Leaf linkage}
+
+At all times, all of a tree's active nodes are reachable from the
+leftmost leaf by \V{next} pointers.
+
+Say that \(n\) and \(n'\) are active nodes in the same tree, with
+\(\N{lowkey}(n) < \N{lowkey}(n')\). Then either \(n\) is reachable from
+\(n'\) by \V{prev} pointers, or \(n\) is dirty (\V{splitting}).
+
+\section{Transaction types}
+
+Leaf values are accessed in read transactions validated by version.
+
+Node modifications use transactions protected by locks.
+
+Internal nodes are accessed during descent using hand-over-hand
+validation by version.
+
+Obtaining a locked pointer to a parent node requires hand-over-hand
+locking with validation.
+
+\section{Get}
+
+Get first descends to the leaf responsible for a key,
+then finds the key in that leaf. This proceeds a layer at a time.
+
+Two related procedures, \N{descend} and \N{advance}, find within a layer
+the leaf node responsible for key $k$. \N{Descend} is more fundamental:
+every lookup involves one or more calls to \N{descend}. \N{Advance} is
+used occasionally to fix lookups that happened concurrently with splits.
+
+Both procedures return a leaf node $n$ and corresponding version $v$.
+The version is never dirty (it was obtained by \N{stable\_version}).
+Either version $v$ of leaf $n$ is responsible for $k$, or
+$v.\V{deleted}$ is true.
+However, neither \N{descend} nor \N{advance} lock nodes or perform any
+writes to shared memory, so $n$ may not \emph{remain} responsible for
+$k$. $n$ could split, moving responsibility for $k$ to the right; or $n$
+could be deleted, moving responsibility for $k$ to the left. Users of
+\N{descend} must detect these situations using read transactions
+validated by a check of $n.\V{version}$. If $n.\V{version}$ indicates a
+split, the user should call \N{advance} to find the leaf truly
+responsible for $k$; if $n.\V{version}$ indicates a deletion, the user
+must call \N{descend} again.
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{descend}(node \V{root}, key $k$) \Returns{\(\langle
+  \N{leaf \V{n}, version \V{v}}\rangle\)}: \\
+\CX{postcondition: either \(v.\V{deleted}\), or version \V{v} of leaf
+\V{n} is responsible for key $k$: \(\N{lowkey}(n) \leq k.\V{slice} <
+\N{highkey}(n_{@v})\)} \\
+retry:
+\> $n \gets \V{root}$;~ $v \gets \text{stable\_version}(n)$ \\
+\> if \(!v.\V{isroot}\): \\
+\>\> \(n \gets \text{true\_root}(n)\);~ goto retry \\
+descend:~
+if $v.\V{isleaf}$: \\
+\>\> return $\langle n, v\rangle$ \\
+\> $n_c \gets \text{child of \(n\) containing \(k\)}$ \\
+\> if $n_c = \NIL$: \C{retry from root if concurrent remove reshaped tree} \\
+\>\> goto retry \\
+\> $v_c \gets \text{stable\_version}(n_c)$ \C{hand-over-hand validation} \\
+\> \fence \\
+\> if $v \oplus \readnow{n.\V{version}} > \V{locked}$: \C{$n$ changed, must retry} \\
+\>\> $\V{oldv} \gets v$;~ $v \gets \text{stable\_version}(n)$ \\
+\>\> if $v.\V{vsplit} \neq \V{oldv}.\V{vsplit}$: \C{retry from root when internal node splits} \\
+\>\>\> goto retry \\
+\> else: \\
+\>\> $n \gets n_c$;~ $v \gets v_c$ \\
+\> goto descend
+\end{programtabbing}
+\label{fig:nlookup}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{advance}(leaf $n$, key $k$) \Returns{\(\langle
+  \N{leaf $n'$, version $v$}\rangle\)}: \\
+\CX{precondition: \(\N{lowkey}(n) \leq k.\V{slice}\)} \\
+\CX{postcondition: either $v.\V{deleted}$ (caller should rerun \N{descend}), or version $v$ of leaf $n'$ is responsible for $k$} \\
+\> $v \gets \text{stable\_version}(n)$;~ $\V{next} \gets n.\V{next}$ \\
+\> while $!v.\V{deleted}$ and $\V{next} \neq \NIL$ and
+       $k \geq \text{lowkey}(\V{next})$: \\
+\>\> $n \gets \V{next}$;~ $v \gets \text{stable\_version}(n)$;~
+       $\V{next} \gets n.\V{next}$ \\
+\> return $\langle n, v \rangle$
+\end{programtabbing}
+\end{figure}
+
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{find\_key}(leaf $n$, key $k$, permutation \V{perm})
+\Returns{\(\langle \N{int \V{i}}, \N{int \(\pi\)} \rangle\)}: \\
+\CX{Returns the insertion index and position of \(k\) in \(n\),
+  considering only key slice and length.} \\
+\CX{postcondition: \(0 \leq i \leq \V{perm}.\V{size}\)} \\
+\CX{postcondition: \(\pi = \NONE\) iff \(k\) is not contained in \(n\)} \\
+\> $i \gets 0$;~ $\V{klength} = \min(k.\V{length}, 9)$ \\
+\> while $i < \V{perm}.\V{size}$: \C{linear search; we use binary search in practice} \\
+\>\> $\pi \gets \V{perm}.\V{pos}[i]$ \\
+\>\> if $n.\V{kslice}[\pi] < k.\V{slice}$ or ($n.\V{kslice}[\pi] =
+k.\V{slice}$ and $n.\V{klength}[\pi] < \V{klength}$): \\
+\>\>\> $++i$ \\
+\>\> else if $n.\V{kslice}[\pi] = k.\V{slice}$ and $n.\V{klength}[\pi] =
+\V{klength}$: \\
+\>\>\> return $\langle i, \pi \rangle$ \\
+\>\> else: \\
+\>\>\> break \\
+\> return $\langle i, \NONE \rangle$
+\end{programtabbing}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{get}(node \V{root}, key $k$): \\
+descend:~
+ $\langle n, v \rangle \gets \text{descend}(\V{root}, k)$ \\
+retry:~
+\> if $v.\V{deleted}$: \\
+\>\> goto descend \\
+\> \(\langle i, \pi \rangle \gets \text{find\_key}(n, k,
+n.\V{permutation})\) \\
+\> if \(\pi \neq \NONE\): \\
+\>\> \(t \gets n.\V{type}[\pi]\);~
+    \(\V{suffix} \gets n.\V{ksuffix}[\pi]\);~
+    \(\V{value} \gets n.\V{value}[\pi]\) \\
+\> \fence \\
+\> if \(v \oplus \readnow{n.\V{version}} > \V{locked}\): \\
+\>\> $\langle n, v \rangle \gets \text{advance}(n, k)$ \\
+\>\> goto retry \\
+\> else if \(\pi \neq \NONE\) and \(t = \LAYER\): \\
+\>\> \(root \gets \V{value}\);~ \(k \gets \N{shift\_key}(k)\) \\
+\>\> goto descend \\
+\> else if \(\pi = \NONE\) or \(\V{suffix} \neq k.\V{suffix}\): \\
+\>\> return \NONE \\
+\> else: \\
+\>\> return \V{value}
+\end{programtabbing}
+\caption{Find the value for a key.}
+\label{fig:nget}
+\end{figure}
+
+
+\section{Put}
+
+Put and remove, unlike get, must lock the leaf containing a key. The
+\N{descend\_and\_lock} function performs a combined traversal and lock.
+Unlike \N{descend}, \N{descend\_and\_lock} descends across multiple
+layers. It returns the unique node that would contain the value for
+\(k\) if \(k\) were in the Masstree.
+
+A natural implementation of \N{descend\_and\_lock} might lock a
+leaf node per layer. When encountering a pointer to a lower layer,
+\N{descend\_and\_lock} would unlock the leaf from the higher layer and
+then recurse into the lower. This, however, would perform far more
+locking operations than required. A goal of Masstree is to perform no
+memory writes for common-case tree traversal. Therefore,
+\N{descend\_and\_lock} includes an optimization that, in the common
+case, recurses into a lower layer without obtaining a lock.
+
+\N{Descend\_and\_lock} also performs a tree cleanup task. Consider a
+full lower-layer tree (linked from a higher layer, which we display
+above the dashed line):
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{insert1_1}
+\end{figure}
+
+\noindent%
+Inserting a new item into this lower layer will cause a split. A new
+leaf node is created,
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{insert1_2}
+\end{figure}
+
+\noindent%
+then a new internal node
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{insert1_3}
+\end{figure}
+
+\noindent%
+and finally a new root.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{insert1_4}
+\end{figure}
+
+\noindent%
+But the higher layer connects to the \emph{old} root, not the new root.
+Correcting this would more work: modifications to the higher layer
+require locking the higher-layer node. We choose to delay that work.
+There's no \emph{correctness} problem with leaving the tree this
+way---thanks to the root reachability invariant, the new root will be
+found by traversing parent pointers from the old. So we leave the tree
+this way until the next put or remove, which will retarget the higher
+layer's pointer.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{insert1_5}
+\end{figure}
+
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{descend\_and\_lock}(node \V{toproot}, key \(k\))
+  \Returns{\(\langle \N{leaf \V{n}}, \N{int \V{i}}, \N{int
+      \(\pi\)}, \N{key \(k'\)}\rangle\)}: \\
+\CX{Find, lock, and return the node into which \(k\) would be inserted.} \\
+\CX{postcondition: \(k' = \N{shift\_key}^m(k)\) for some \(m \geq 0\)}\\
+\CX{postcondition: \(n\) is locked and \(n\) is responsible for \(k'\)}\\
+\CX{postcondition: \(i\) is the insertion index for \(k'\) in \(n\)}\\
+\CX{postcondition: \(\pi = \NONE\) iff \(k'\) is not present in \(n\)}\\
+retrytop:~
+   $\V{root} \gets \V{toproot}$;~ \(k \gets \N{reset\_key}(k)\) \\
+descend:~  $\langle n, v \rangle \gets \text{descend}(\V{root}, k)$ \\
+\> if $k$ has suffix: \C{optimization: avoid some unnecessary traversal locks} \\
+\>\> \(x \gets \text{try\_descend\_layer}(n, v, k)\) \\
+\>\> if \(x \neq \NONE\): \\
+\>\>\> \(\V{root} \gets x\);~ \(k \gets \N{shift\_key}(k)\) \\
+\>\>\> goto descend \\
+%
+retry:~
+\> \(\N{lock}(n)\);~ \(v' \gets n.\V{version}\) \\
+\> if \(v'.\V{deleted}\): \\
+\>\> unlock(\(n\));~ goto descend \\
+\> else if \(n.\V{deletedlayer}\): \C{an entire layer has been
+  deleted, start over}\\
+\>\> unlock(\(n\));~ goto retrytop \\
+\> else if \(v'.\V{vsplit} \neq v.\V{vsplit}\) and \(n.\V{next} \neq
+\NIL\) and \(k \geq \N{lowkey}(n.\V{next})\): \\
+\>\> \(\V{next} \gets n.\V{next}\);~ unlock(\(n\)) \\
+\>\> \(\langle n, v \rangle \gets \N{advance}(\V{next}, k)\) \\
+\>\> goto retry \\
+\> else: \\
+\>\> \(\langle i, \pi \rangle \gets \N{find\_key}(n, k, n.\V{permutation})\)
+\\
+\>\> if \(\pi \neq \NONE\) and \(n.\V{type}[\pi] = \LAYER\): \\
+\>\>\> \(\V{root} \gets n.\V{value}[\pi]\);~ \(k \gets \N{shift\_key}(k)\) \\
+\>\>\> if \(!\V{root}.\V{isroot}\): \\
+\>\>\>\> \(\V{root} \gets n.\V{value}[\pi] \gets \N{true\_root}(\V{root})\) \\
+\>\>\> unlock(\(n\));~ goto descend \\
+\>\> else: \\
+\>\>\> return \(\langle n, i, \pi, k \rangle\) \\
+\programtabskip
+%
+\textbf{try\_descend\_layer}(node \(n\), version \(v\), key \(k\)): \\
+\> \(\langle i, \pi \rangle \gets \text{find\_key}(n, k, n.\V{permutation})\) \\
+\> if \(\pi \neq \NONE\): \\
+\>\> \(t \gets n.\V{type}[\pi]\) \\
+\>\> \fence \\
+\>\> \(\V{value} \gets n.\V{value}[\pi]\) \\
+\>\> \fence \\
+\>\> if \(!v.\V{deleted}\) and \(t = \LAYER\) and \(v \oplus \readnow{n.\V{version}} \leq \V{locked}\)
+and \(\V{value}.\V{isroot}\): \\
+\>\>\> return \V{value} \\
+\> return \NONE
+\end{programtabbing}
+\caption{Lock and return the leaf that would contain a key.}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{put}(node \V{root}, key \(k\), value \(\V{value}\)): \\
+\> \(\langle n, i, \pi, k \rangle \gets \N{descend\_and\_lock}(\V{toproot}, k)\) \\
+retry:~
+\> if \(\pi \neq \NONE\) and \(n.\V{ksuffix}[\pi] = k.\V{suffix}\): \\
+\>\> fork \(\N{free\_value}(n.\V{value}[\pi])\) \\
+\>\> \(n.\V{value}[\pi] \gets \V{value}\) \\
+\>\> unlock(\(n\));~ return \\
+\> else if \(\pi \neq \NONE\): \\
+\>\> \(n \gets \N{new\_layer}(n, \pi)\);~ \(k \gets \N{shift\_key}(k)\) \\
+\>\> \(\langle i, \pi \rangle \gets \N{find\_key}(n, k,
+n.\V{permutation})\) \\
+\>\> goto retry \\
+\> if \(n.\V{modstate} \neq \INSERTING\): \\
+\>\> \(n.\V{modstate} \gets \INSERTING\) \\
+\>\> \(n.\V{version}.\V{inserting} \gets 1\) \\
+\>\> \fence \\
+\> if \(n.\V{size} < \LEAFWIDTH\): \\
+\>\> \(\pi \gets \text{an unused position from \(n.\V{permutation}\)}\) \\
+\>\> \(n.\V{kslice}[\pi] \gets k.\V{slice}\) \\
+\>\> \(n.\V{ksuffix}[\pi] \gets k.\V{suffix}\) \\
+\>\> \(n.\V{klength}[\pi] \gets \min(k.\V{length}, 9)\) \\
+\>\> \(n.\V{type}[\pi] \gets \VALUE\) \\
+\>\> \(n.\V{value}[\pi] \gets \V{value}\) \\
+\>\> \fence \\
+\>\> \(n.\V{permutation} \gets \text{modified permutation with \(\pi\) at
+  position \(i\)}\) \\
+\>\> unlock(\(n\));~ return \\
+\> \(\N{split}(n, k, \V{value})\) \\
+\programtabskip
+%
+\textbf{new\_layer}(leaf \(n\), int \(\pi\)): \\
+\> \(\V{suffix} = n.\V{ksuffix}[\pi]\);~ \(\V{value} \gets
+n.\V{value}[\pi]\) \\
+\> \(n' \gets \text{new locked leaf containing only \V{suffix} and
+  \V{value}}\) \\
+\> \(n.\V{version}.\V{inserting} \gets 1\) \\
+\> \fence \\
+\> \(n.\V{value}[\pi] \gets n'\) \\
+\> \(n.\V{ksuffix}[\pi] \gets \text{empty string}\) \\
+%\> \fence \\
+\> \(n.\V{type}[\pi] \gets \LAYER\) \\
+\> unlock(\(n\)) \\
+\> return \(n'\)
+\end{programtabbing}
+\caption{Put implementation (except for split).}
+\end{figure}
+
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{split}(node $n$, key $k$, value \V{value}): \C{precondition: $n$ locked} \\
+\> $n' \gets \text{new empty leaf}$ \\
+\> \(n'.\V{version} \gets n.\V{version}\) \C{\(n'\) is initially locked} \\
+\> \(n'.\V{version}.\V{splitting} \gets 1\) \\
+\> copy the larger keys and values from \(n\) to \(n'\) \\
+\> assign \(n'.\V{permutation}\) appropriately \\
+\> \fence \\
+\> \(\N{link\_split\_leaf}(n, n')\) \\
+\> \(n.\V{version}.\V{splitting} \gets 1\) \\
+\> \fence \\
+\> assign \(n.\V{permutation}\) to contain only those keys not copied to
+\(n'\) \\
+\> insert \(k\) and \V{value} into \(n\) or \(n'\) as appropriate \\
+ascend:~
+\> $p \gets \text{locked\_parent}(n)$ \C{hand-over-hand locking} \\
+\> if $p = \NIL$: \C{$n$ was old root} \\
+\>\> \(p \gets \text{new interior node}\) \\
+\>\> insert \(n\), \(n'\) into \(p\) \\
+\>\> \(p.\V{isroot} \gets 1\);~ \(p.\V{parent} \gets \NIL\) \\
+\>\> \(n'.\V{parent} \gets p\) \\
+\>\> \fence \\
+\>\> \(n.\V{parent} \gets p\) \\
+\>\> unlock($n$);~ unlock($n'$);~ return \\
+\> else if $p$ is not full: \\
+\>\> $p.\V{version}.\V{inserting} \gets 1$ \\
+\>\> \fence \\
+\>\> \(\N{unlock}(n)\) \\
+\>\> shift $p$ keys and children to include $n'$ \\
+\>\> \(n'.\V{parent} \gets p\) \\
+\>\> \fence \\
+\>\> \(++p.\V{size}\) \\
+\>\> unlock($n$);~ unlock($n'$);~ unlock($p$);~ return \\
+\> else: \\
+\>\> \(p.\V{version}.\V{splitting} \gets 1\) \\
+\>\> \fence \\
+\>\> \(\N{unlock}(n)\) \\
+\>\> \(p' \gets \text{new interior node}\) \\
+\>\> \(p'.\V{version} \gets p.\V{version}\) \\
+\>\> \fence \\
+\>\> move larger keys from \(p\) into \(p'\), inserting \(n'\) where it belongs  (and setting \(n'.\V{parent}\)) \\
+\>\> for all \(c \in \text{children of \(p'\)}\): \\
+\>\>\> \(c.\V{parent} \gets p'\) \\
+\>\> unlock($n'$);~ $n \gets p$;~ $n' \gets p'$;~ goto ascend
+\end{programtabbing}
+\caption{Split a leaf and insert a key.}
+\label{fig:nsplit}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{link\_split\_leaf}(leaf \(n\), leaf \(n'\)): \\
+\> \(n'.\V{prev} \gets n\) \\
+\> \(\V{next} \gets n.\V{next}\) \\
+\> while \(\V{next} \neq \NIL\) and
+(\V{next} has a \MARK\ or \(!cmpxchg(n.\V{next}, \V{next},
+\V{next} + \MARK)\)): \\
+\>\> \(\V{next} \gets \readnow{n.\V{next}}\) \\
+\> \(n'.\V{next} \gets \V{next}\) \\
+\> if \(\V{next} \neq \NIL\): \\
+\>\> \(\V{next}.\V{prev} \gets n'\) \\
+\> \fence \\
+\> \(n.\V{next} \gets n'\)
+\end{programtabbing}
+\end{figure}
+
+\section{Remove}
+
+The most complex Masstree operation is remove. It's complex because of
+the wide range of tree changes it can cause.
+
+\subsection{Removing an item}
+
+We start with a simple example tree.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_1}
+\end{figure}
+
+Removing a single item---say, \ITEM{e}---is relatively simple: all code
+is in two short functions, \N{remove} and \N{remove\_item}. We first use
+\N{descend\_and\_lock} to locate and lock the leaf containing the item, then pop
+the item out of the relevant leaf's permutation.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_2}
+\end{figure}
+
+\noindent
+The heavy circle on the leaf's upper left indicates a lock. The item's
+value is reclaimed by \N{free\_value}, but only after concurrent readers
+have completed.
+
+Note that \N{remove} does not actually modify the leaf's state for the
+item, namely \(n.\V{kslice}[\pi]\), \(n.\V{klength}[\pi]\),
+\(n.\V{ksuffix}[\pi]\), \(n.\V{type}[\pi]\), and \(n.\V{value}[\pi]\)!
+This allows concurrent readers who saw the \emph{old} permutation to
+extract a correct value for the item, namely the old value. This is
+correct---an overlapping reader can return the old value. Alternatively,
+\N{remove} could dirty the node, but this would force readers to retry,
+something we aim to avoid.
+
+However, there is a wrinkle. We must ensure that a change in the node's keys
+can be detected by examining the node's version---specifically, by examining
+the version and the current node size. Thus, the sequence of operations
+``insert(\textsc{x}); remove(\textsc{x})'' should change the node's version
+number. To this end, we keep track of a per-node modification state, called
+\(n.\V{modstate}\). This is either \INSERTING, if we are currently in the
+midst of inserting keys into the node, or \REMOVING, if we're currently
+removing keys from the node. Switching between modification states requires an
+explicit change to the node's version number.
+
+\subsection{Removing a leaf}
+
+A series of removes can empty out a leaf entirely. Empty nodes would
+waste memory and slow down lookup, so they are removed from the tree.
+
+The set of leaves in a Masstree are collectively responsible for a full
+range of keys. When a leaf is removed, responsibility for its key range
+shifts to another leaf. Since \N{lowkey} values never change,
+responsibility always passes to the left, to the node's predecessor in
+the doubly linked list. Remove must ensure that concurrent operations
+reach this predecessor. This requires remove to update both the leaf
+linked list, which is used during scans and some puts, and the
+\Bplus\ tree structure.
+
+As an exception, \N{remove} always preserves the leftmost leaf in a
+tree. This simplifies many operations and is logically required by our
+invariants: \N{lowkey} values never change, but deleting the leftmost
+node would effectively require setting its successor's \N{lowkey} to
+$-\infty$. The leftmost leaf can be deleted, but only when the entire
+subtree is empty.
+
+We demonstrate by removing all the elements in \ITEM{def}.
+
+Masstree first marks the node's version as \V{deleted}, which we show
+with a white X. Any operation that reaches a \V{deleted} node will retry
+from the root. This is important because the next steps will unlink the
+deleted node from the tree, and unlinked nodes no longer receive updates
+about other changes to the tree's structure. This means there is no
+reliable way to get back on track from a deleted node. Other operations
+will retry in a loop until the deleted leaf is fully unlinked. Also, a
+callback is registered to free the leaf's memory once concurrent readers
+have completed.
+
+We next remove the node from the leaf linked list. Logical ``locks''
+protect against neighbors' concurrent removes and splits, but to prevent
+deadlock, these per-node locks are different from the main per-node
+locks. The lock is a mark bit stolen from node \V{next} pointers. The mark
+bit in \(n.\V{next}\) protects the setting of \(n.\V{next}\), and the
+setting of \(n.\V{next}.\V{prev}\).
+
+The \N{unlink\_leaf} function contains the code. (The code for inserting
+new leaves during split is in \N{link\_split\_leaf}.) First, the
+\(\ITEM{def}.\V{next}\) pointer is locked with a mark, shown in black
+below. This lock protects both \(\ITEM{def}.\V{next}\) and
+\(\ITEM{ghi}.\V{prev}\), so \ITEM{ghi} now cannot remove itself from the
+list until \ITEM{def} is unlinked.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_3}
+\end{figure}
+
+\noindent%
+But removing \ITEM{def} requires modifying, and therefore locking,
+\(\ITEM{def}.\V{prev}.\V{next} = \ITEM{abc}.\V{next}\) as well. This
+modification requires a retry loop, in case \ITEM{abc} splits or is
+removed during the operation.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_31}
+\end{figure}
+
+\noindent%
+With \(\ITEM{def}.\V{prev}.\V{next}\) and \(\ITEM{def}.\V{next}\) both
+locked, the unlink is easy. First, \(\ITEM{ghi}.\V{prev}\) is modified,
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_4}
+\end{figure}
+
+\noindent%
+and then, after a fence, \(\ITEM{abc}.\V{next}\). A single assignment
+both unlocks and changes the pointer.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_5}
+\end{figure}
+
+\noindent%
+This algorithm resembles some algorithms for lock-free
+doubly-linked-list insertion and removal, but it is a bit simpler. This
+is because Masstree leaf insertion is constrained---it only happens
+during split. As a result, insertion immediately after \(n\) cannot
+proceed concurrently with removal of \(n\).
+
+The leaf linked list is correct, but the \Bplus\ tree index is not.
+Masstree traverses to and locks the leaf's parent, then releases the
+lock on the leaf. It dirties the interior node, searches for the deleted
+leaf's \N{lowkey} (here, \(\N{lowkey}(\ITEM{def}) = \ITEM{d}\)), and removes
+the corresponding key and child pointer, creating this state.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_6}
+\end{figure}
+
+And this completes the removal of \ITEM{def}.
+
+\subsection{Collapsing trivial interior nodes}
+
+Now say \ITEM{ghi} is deleted. This process starts out as before.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_7}
+\end{figure}
+
+\noindent%
+But when \(\N{lowkey}(\ITEM{ghi}) = \ITEM{g}\) is removed from its
+parent interior node, that interior node becomes \emph{trivial}: it has
+no keys and only one child. Trivial interior nodes are redundant,
+wasting space and traversal time.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_8}
+\end{figure}
+
+Masstree detects trivial nodes and deletes them in the \N{collapse}
+function. \N{Collapse} marks trivial nodes' versions as \V{deleted} and
+frees them after the usual reader delay. It also walks up the tree, with
+hand-over-hand locking, and adjusts parent nodes' pointers to hop over
+the deleted trivial node.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove1_9}
+\end{figure}
+
+\noindent%
+This deletion procedure leaves the tree funnily shaped: leaves are at
+different heights. It's this property that prevents us from storing
+links on interior nodes.
+
+The \N{collapse} function won't ever delete the root node. A trivial
+root requires more work to fix, as we'll see.
+
+\subsection{Reshaping the tree}
+
+We return to the original example and, this time, remove leaf \ITEM{jkl}.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_1}
+\end{figure}
+
+Now, \ITEM{jkl} is the \emph{first} child of its parent. This requires
+special handling. To see why, note that
+the usual removal procedure would create this state:
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_2}
+\end{figure}
+
+\noindent%
+Now consider a \N{put} operation on key \ITEM{j}. This \N{put} should
+reach leaf \ITEM{ghi}, which has inherited responsibility for
+the \ITEM{jkl} range. But the \Bplus\ tree structure will direct the
+\N{put} to leaf \ITEM{mno}!
+
+Although such mistakes might be detectable at the leaves
+using \N{lowkey} comparisons, we prefer to avoid the problem
+in a different way. When
+a subtree's leftmost leaf is deleted, Masstree detects the issue and
+switches to a special \N{reshape} procedure. This procedure first sets
+the subtree's leftmost child pointer to \(\NIL\), causing concurrent
+gets and puts that would hit that node to retry from the root.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_3}
+\end{figure}
+
+The procedure then works up the tree using hand-over-hand locking. At
+each step it replaces the deleted lowkey, here \ITEM{j}, with its
+logical successor, here \ITEM{m}. In this tree, that takes one step, but
+more complicated trees might require multiple replacements.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_4}
+\end{figure}
+
+This tree is valid and sends no puts down the wrong path. Although an
+interior node has a redundant key---the right-hand interior node
+contains key \ITEM{m}, although it is responsible for no keys less
+than \ITEM{m}---we don't bother to clean it up yet.
+
+\subsection{Root trimming}
+
+When the \ITEM{mno} leaf is deleted,
+the entire right-hand subtree becomes redundant.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_5}
+\end{figure}
+
+After removing the \ITEM{mno} reference, \N{remove\_leaf} can tell that
+the right-hand interior node is redundant: its single child pointer is
+\NIL. The interior node can therefore be deleted
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_6}
+\end{figure}
+
+\noindent%
+and this continues
+up the tree.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_7}
+\end{figure}
+
+\noindent%
+But now the \emph{root} node is trivial, having only a single child. How
+can we fix this? The root node may be referenced by
+a leaf node in a higher \emph{tree layer}, as shown here:
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_8}
+\end{figure}
+
+\noindent%
+(Everything above the dashed line is in the next-higher layer.) We
+cannot delete the root node until this pointer is switched to its child.
+And any change to that higher layer's values requires locking the
+corresponding leaf.
+
+We solve this problem by registering a callback to trim the layer,
+called \N{gclayer}. This function locks the
+relevant leaf in the higher layer
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_9}
+\end{figure}
+
+\noindent%
+locks the lower layer's root
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_11}
+\end{figure}
+
+\noindent%
+and then marks its single child as the new root, switching the higher
+layer's value pointer to it.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_12}
+\end{figure}
+
+The original root is now garbage and can be reclaimed.
+
+But what about concurrent readers? The root reachability invariant
+requires that the true root of a layer always be reachable from any
+prior root. We therefore set the \emph{old} root's parent pointer to
+point to the \emph{new} root. This can cause an apparent anomaly: an
+internal node can have a leaf as its parent!
+
+\subsection{Deleting a layer}
+
+After the last item in a layer is removed, Masstree can remove the
+entire layer. This starts out like any gclayer call: the relevant leaf
+in the higher layer is locked
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_15}
+\end{figure}
+
+\noindent
+and so is the empty root in the lower layer.
+
+\begin{figure}[H]
+\includegraphics[scale=.8]{remove2_16}
+\end{figure}
+
+\noindent%
+But the empty root is not deleted in the conventional way. We mark it
+not as deleted, but as a \emph{deleted layer}, using
+a flag that's checked only by concurrent \emph{writers}. Why?
+Normally, a lookup encountering a deleted node retries from the root of
+the current layer. Marking a layer's only node as deleted would cause a
+retry loop! Instead, concurrent readers that reach a deleted layer see a
+normal empty leaf and return ``not found.'' (This is correct: the
+deleted layer contains no keys.) Concurrent writers, however, must not
+write into a deleted layer, lest their updates be lost. On encountering
+a deleted layer, writers retry from the layer-0 root.
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{remove}(node \V{toproot}, key \(k\)): \\
+\> \(\langle n, i, \pi, k \rangle \gets \N{descend\_and\_lock}(\V{toproot}, k)\) \\
+\> if \(\pi = \NONE\) or \(n.\V{ksuffix}[\pi] \neq k.\V{suffix}\): \\
+\>\> return \FALSE \\
+\> \(\N{remove\_item}(n, i, \pi, k, \V{toproot})\) \\
+\> return \TRUE
+\end{programtabbing}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{remove\_item}(leaf \(n\), int \(i\), int \(\pi\), key \(k\), node
+\V{toproot}): \\
+\> if \(n.\V{modstate} = \INSERTING\): \\
+\>\> \(n.\V{modstate} \gets \REMOVING\) \\
+\>\> \(n.\V{version}.\V{inserting} \gets 1 \) \\
+\>\> \fence \\
+\> \(n.\V{permutation} \gets \N{permremove}(n.\V{permutation}, i)\) \\
+\> fork \(\N{free\_value}(n.\V{value}[\pi])\) \\
+\> if \(n.\V{size} \neq 0\): \\
+\>\> \(\N{unlock}(n)\);~ return \\
+\> \(\N{remove\_leaf}(n, \V{toproot}, k.\V{layerprefix})\)
+\end{programtabbing}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{remove\_leaf}(leaf \V{n}, node \V{toproot}, key \V{layerkey}): \\
+\> if \(n.\V{prev} = \NIL\): \\
+\>\> if \(n.\V{next} = \NIL\) and \(\V{layerkey}\) is not empty: \\
+\>\>\> fork \(\N{gclayer}(\V{toproot}, \V{layerkey})\) \\
+\>\> unlock(\(n\)) \\
+\>\> return \\
+\> \(n.\V{version}.\V{deleted} \gets 1\);~ fork \(\N{free\_node}(n)\) \\
+\> \(\N{unlink\_leaf}(n)\) \\
+\> \(k \gets \N{lowkey}(n)\) \\
+ascend:~
+\> \(p \gets \N{locked\_parent}(n)\);~ \(\N{unlock}(n)\) \\
+\> \(i \gets \text{position of \V{k} in \V{p}}\) \\
+\> if \(i \neq 0\): \\
+\>\> \(p.\V{version}.\V{inserting} \gets 1\) \\
+\>\> \fence \\
+\>\> remove the element at index \(i - 1\), shifting others down \\
+\>\> if \(i \neq 1\) or \(p.\V{child}[0] \neq \NIL\): \\
+\>\>\> \(\N{collapse}(p, k, \V{toproot}, \V{layerkey})\) \\
+\>\>\> return \\
+\> if \(p.\V{size} = 0\): \\
+\>\> \(p.\V{version}.\V{deleted} \gets 1\);~ fork \(\N{free\_node}(p)\) \\
+\> else: \\
+\>\> \(\N{reshape}(p, k, \V{toproot}, \V{layerkey})\) \\
+\>\> return \\
+\> \(n \gets p\);~ goto ascend
+\end{programtabbing}
+\caption{Remove a node.}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{unlink\_leaf}(leaf \(n\)): \\
+\> \(\V{next} \gets n.\V{next}\) \\
+\> while \(\V{next} \neq \NIL\) and \(!\N{cmpxchg}(n.\V{next}, \V{next},
+               \V{next} + \MARK)\): \\
+\>\> \(\V{next} \gets \readnow{n.\V{next}}\) \\
+\> \(\V{prev} \gets n.\V{prev}\) \\
+\> while \(!\N{cmpxchg}(\V{prev}.\V{next}, n, n + \MARK)\): \\
+\>\> \(\V{prev} \gets \readnow{n.\V{prev}}\) \\
+\> if \(\V{next} \neq \NIL\): \\
+\>\> \(\V{next}.\V{prev} \gets \V{prev}\) \\
+\> \fence \\
+\> \(\V{prev}.\V{next} \gets \V{next}\)
+\end{programtabbing}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{reshape}(interiornode \(n\), key \V{k}, node \V{toproot}, key \V{layerkey}): \\
+\> \(n.\V{child}[0] \gets \NIL\) \\
+\> \(\V{patchkey} \gets n.\V{kslice}[0]\) \\
+ ascend:~
+\> \(p \gets \N{locked\_parent}(n)\);~ \(\N{unlock}(n)\) \\
+\> \(i \gets \text{position of \V{k} in \V{p}}\) \\
+\> if \(i \neq 0\): \\
+\>\> \(p.\V{version}.\V{inserting} \gets 1\) \\
+\>\> \fence \\
+\>\> \(p.\V{kslice}[i - 1] \gets \V{patchkey}\) \\
+\>\> if \(i \neq 1\) or \(p.\V{child}[0] \neq \NIL\): \\
+\>\>\> \(\N{collapse}(p, k, \V{toproot}, \V{layerkey})\) \\
+\>\>\> return \\
+\> \(n \gets p\);~ goto ascend
+\end{programtabbing}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{collapse}(interiornode \(n\), key \(k\), node \V{toproot},
+key \V{layerkey}): \\
+ascend:~
+\> if \(n.\V{size} \neq 0\): \\
+\>\> \(\N{unlock}(n)\);~ return \\
+\> \(p \gets \N{locked\_parent}(n)\) \\
+\> if \(p = \NIL\): \\
+\>\> if \V{layerkey} is not empty: \\
+\>\>\> fork gclayer(\V{toproot}, \V{layerkey}) \\
+\>\> \(\N{unlock}(n)\);~ return \\
+\> \(i \gets \text{position of \(k\) in \(p\)}\) \\
+\> \(p.\V{child}[i] \gets n.\V{child}[0]\) \\
+\> \(n.\V{child}[0].\V{parent} \gets p\) \\
+\> \(n.\V{version}.\V{deleted} \gets 1\);~ fork \(\N{free\_node}(n)\) \\
+\> \(\N{unlock}(n)\);~ \(n \gets p\);~ goto ascend
+\end{programtabbing}
+\end{figure}
+
+\begin{figure}[H]
+\begin{programtabbing}
+\textbf{gclayer}(node \V{toproot}, key \V{layerkey}): \\
+\> \rcufence \\
+\> \(\langle n, i, \pi, \N{\_} \rangle \gets \N{descend\_and\_lock}(\V{toproot}, \V{layerkey})\) \\
+\> if \(\pi = \NONE\) or \(n.\V{type}[\pi] \neq \LAYER\): \\
+\>\> \(\N{unlock}(n)\);~ return \\
+retry:~
+\> \(\V{layer} \gets n.\V{value}[\pi]\) \\
+\> if \(!\V{layer}.\V{isroot}\): \\
+\>\> \(\V{layer} \gets n.\V{value}[\pi] \gets \N{true\_root}(\V{layer})\) \\
+\> if \(\V{layer}.\V{size} > 0\) and \(\V{layer}.\V{isroot}\): \\
+\>\> \(\N{unlock}(n)\);~ return \\
+\> \(\N{lock}(\V{layer})\) \\
+\> if \(!\V{layer}.\V{isroot}\) and \(\V{layer}.\V{parent} = \NIL\): \\
+\>\> \(\V{layer}.\V{isroot} \gets 1\) \\
+\> if \(\V{layer}.\V{size} > 0\) or \(!\V{layer}.\V{isroot}\): \\
+\>\> \(\N{unlock}(\V{layer})\);~ \(\N{unlock}(\V{n})\);~ return \\
+\> if \(\V{layer}.\V{isleaf}\): \\
+\>\> \(\V{layer}.\V{deletedlayer} \gets 1\) \\
+\>\> \(\N{unlock}(\V{layer})\) \\
+\>\> \(\N{remove\_item}(n, i, \pi, \V{layerkey}, \V{toproot})\) \\
+\>\> return \\
+\> else: \\
+\>\> \(\V{child} \gets \V{layer}.\V{child}[0]\) \\
+\>\> \(\V{child}.\V{parent} \gets \NIL\) \\
+\>\> \(n.\V{value}[\pi] \gets \V{child}\) \\
+\>\> \(\V{layer}.\V{isroot} \gets 0\) \\
+\>\> \(\V{layer}.\V{parent} \gets \V{child}\) \\
+\>\> \(\N{unlock}(\V{layer})\) \\
+\>\> fork \(\N{free\_node}(\V{layer})\) \\
+\>\> goto retry
+\end{programtabbing}
+\caption{Remove a twig.}
+\end{figure}
+
+\section{Other optimizations and left-off properties}
+
+In the implementation, \(\N{lowkey}(n)\) is stored in
+\(n.\V{kslice}[0]\).
+%
+Thus, the code must ensure that any values stored in position 0
+have this slice.
+%
+This isn't a problem unless the value in position 0 is removed.
+%
+To guard against inappropriate reuse, the \N{insert} procedure
+ensures that position 0 is only reused by a key with the right slice.
+
+In the implementation, \(n.\V{type}\) is stored implicitly; its values
+are inferred from \(n.\V{klength}\).
+
+
+node\_ts
+
+\end{document}
diff --git a/silo/masstree/file.cc b/silo/masstree/file.cc
new file mode 100644 (file)
index 0000000..7586878
--- /dev/null
@@ -0,0 +1,92 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "file.hh"
+#include "straccum.hh"
+#include <fcntl.h>
+#include <stdio.h>
+
+lcdf::String read_file_contents(int fd) {
+    lcdf::StringAccum sa;
+    while (1) {
+        char *buf = sa.reserve(4096);
+        if (!buf) {
+            errno = ENOMEM;
+            return lcdf::String();
+        }
+
+        ssize_t x = read(fd, buf, 4096);
+        if (x != -1 && x != 0)
+            sa.adjust_length(x);
+        else if (x == 0)
+            break;
+        else if (errno != EINTR)
+            return lcdf::String();
+    }
+
+    errno = 0;
+    return sa.take_string();
+}
+
+lcdf::String read_file_contents(const char *filename) {
+    int fd = open(filename, O_RDONLY);
+    if (fd == -1)
+        return lcdf::String();
+
+    lcdf::String text = read_file_contents(fd);
+
+    if (text.empty() && errno) {
+        int saved_errno = errno;
+        close(fd);
+        errno = saved_errno;
+    }
+    return text;
+}
+
+int sync_write_file_contents(const char *filename, const lcdf::String &contents,
+                             mode_t mode)
+{
+    int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, mode);
+    if (fd == -1)
+        return -1;
+
+    ssize_t x = safe_write(fd, contents.data(), contents.length());
+    if (x != contents.length()) {
+    error:
+        close(fd);
+        return -1;
+    }
+
+    int r = fsync(fd);
+    if (r != 0)
+        goto error;
+
+    r = close(fd);
+    if (r != 0)
+        goto error;
+
+    return 0;
+}
+
+int atomic_write_file_contents(const char *filename, const lcdf::String &contents,
+                               mode_t mode)
+{
+    lcdf::String tmp_filename = lcdf::String(filename) + ".tmp";
+    int r = sync_write_file_contents(tmp_filename.c_str(), contents, mode);
+    if (r != 0)
+        return -1;
+
+    return rename(tmp_filename.c_str(), filename);
+}
diff --git a/silo/masstree/file.hh b/silo/masstree/file.hh
new file mode 100644 (file)
index 0000000..365d0f7
--- /dev/null
@@ -0,0 +1,82 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVDB_FILE_HH
+#define KVDB_FILE_HH 1
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include "string.hh"
+
+inline ssize_t
+safe_read(int fd, void *buf, size_t count)
+{
+    size_t pos = 0;
+    while (pos != count) {
+        ssize_t x = ::read(fd, buf, count - pos);
+        if (x != -1 && x != 0) {
+            buf = reinterpret_cast<char *>(buf) + x;
+            pos += x;
+        } else if (x == 0)
+            break;
+        else if (errno != EINTR && pos == 0)
+            return -1;
+        else if (errno != EINTR)
+            break;
+    }
+    return pos;
+}
+
+inline ssize_t
+safe_write(int fd, const void *buf, size_t count)
+{
+    size_t pos = 0;
+    while (pos != count) {
+        ssize_t x = ::write(fd, buf, count - pos);
+        if (x != -1 && x != 0) {
+            buf = reinterpret_cast<const char *>(buf) + x;
+            pos += x;
+        } else if (x == 0)
+            break;
+        else if (errno != EINTR && pos == 0)
+            return -1;
+        else if (errno != EINTR)
+            break;
+    }
+    return pos;
+}
+
+inline void
+checked_write(int fd, const void *buf, size_t count)
+{
+    ssize_t x = safe_write(fd, buf, count);
+    always_assert(size_t(x) == count);
+}
+
+template <typename T> inline void
+checked_write(int fd, const T *x)
+{
+    checked_write(fd, reinterpret_cast<const void *>(x), sizeof(*x));
+}
+
+
+lcdf::String read_file_contents(int fd);
+lcdf::String read_file_contents(const char *filename);
+int sync_write_file_contents(const char *filename, const lcdf::String &contents,
+                             mode_t mode = 0666);
+int atomic_write_file_contents(const char *filename, const lcdf::String &contents,
+                               mode_t mode = 0666);
+
+#endif
diff --git a/silo/masstree/hashcode.hh b/silo/masstree/hashcode.hh
new file mode 100644 (file)
index 0000000..d945779
--- /dev/null
@@ -0,0 +1,119 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef CLICK_HASHCODE_HH
+#define CLICK_HASHCODE_HH
+#include <stddef.h>
+#include <inttypes.h>
+#if HAVE_STD_HASH
+#include <functional>
+#endif
+
+// Notes about the hashcode template: On GCC 4.3.0, "template <>" is required
+// on the specializations or they aren't used.  Just plain overloaded
+// functions aren't used.  The specializations must be e.g. "const char &",
+// not "char", or GCC complains about a specialization not matching the
+// general template.  The main template takes a const reference for two
+// reasons.  First, providing both "hashcode_t hashcode(T)" and "hashcode_t
+// hashcode(const T&)" leads to ambiguity errors.  Second, providing only
+// "hashcode_t hashcode(T)" is slower by looks like 8% when T is a String,
+// because of copy constructors; for types with more expensive non-default
+// copy constructors this would probably be worse.
+
+typedef size_t hashcode_t;     ///< Typical type for a hashcode() value.
+
+template <typename T>
+inline hashcode_t hashcode(T const &x) {
+    return x.hashcode();
+}
+
+template <>
+inline hashcode_t hashcode(char const &x) {
+    return x;
+}
+
+template <>
+inline hashcode_t hashcode(signed char const &x) {
+    return x;
+}
+
+template <>
+inline hashcode_t hashcode(unsigned char const &x) {
+    return x;
+}
+
+template <>
+inline hashcode_t hashcode(short const &x) {
+    return x;
+}
+
+template <>
+inline hashcode_t hashcode(unsigned short const &x) {
+    return x;
+}
+
+template <>
+inline hashcode_t hashcode(int const &x) {
+    return x;
+}
+
+template <>
+inline hashcode_t hashcode(unsigned const &x) {
+    return x;
+}
+
+template <>
+inline hashcode_t hashcode(long const &x) {
+    return x;
+}
+
+template <>
+inline hashcode_t hashcode(unsigned long const &x) {
+    return x;
+}
+
+template <>
+inline hashcode_t hashcode(long long const &x) {
+    return (x >> 32) ^ x;
+}
+
+template <>
+inline hashcode_t hashcode(unsigned long long const &x) {
+    return (x >> 32) ^ x;
+}
+
+#if HAVE_INT64_TYPES && !HAVE_INT64_IS_LONG && !HAVE_INT64_IS_LONG_LONG
+template <>
+inline hashcode_t hashcode(int64_t const &x) {
+    return (x >> 32) ^ x;
+}
+
+template <>
+inline hashcode_t hashcode(uint64_t const &x) {
+    return (x >> 32) ^ x;
+}
+#endif
+
+template <typename T>
+inline hashcode_t hashcode(T * const &x) {
+    return reinterpret_cast<uintptr_t>(x) >> 3;
+}
+
+template <typename T>
+inline typename T::key_const_reference hashkey(const T &x) {
+    return x.hashkey();
+}
+
+#endif
diff --git a/silo/masstree/json.cc b/silo/masstree/json.cc
new file mode 100644 (file)
index 0000000..5a84f51
--- /dev/null
@@ -0,0 +1,1226 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+// -*- c-basic-offset: 4 -*-
+#include "json.hh"
+#include "compiler.hh"
+#include <ctype.h>
+namespace lcdf {
+
+/** @class Json
+    @brief Json data.
+
+    The Json class represents Json data: null values, booleans, numbers,
+    strings, and combinations of these primitives into arrays and objects.
+
+    Json objects are not references, and two Json values cannot share
+    subobjects. This differs from Javascript. For example:
+
+    <code>
+    Json j1 = Json::make_object(), j2 = Json::make_object();
+    j1.set("a", j2); // stores a COPY of j2 in j1
+    j2.set("b", 1);
+    assert(j1.unparse() == "{\"a\":{}}");
+    assert(j2.unparse() == "{\"b\":1}");
+    </code>
+
+    Compare this with the Javascript code:
+
+    <code>
+    var j1 = {}, j2 = {};
+    j1.a = j2; // stores a REFERENCE to j2 in j1
+    j2.b = 1;
+    assert(JSON.stringify(j1) == "{\"a\":{\"b\":1}}");
+    </code>
+
+    Most Json functions for extracting components and typed values behave
+    liberally. For example, objects silently convert to integers, and
+    extracting properties from non-objects is allowed. This should make it
+    easier to work with untrusted objects. (Json objects often originate
+    from untrusted sources, and may not have the types you expect.) If you
+    prefer an assertion to fail when a Json object has an unexpected type,
+    use the checked <tt>as_</tt> and <code>at</code> functions, rather than
+    the liberal <tt>to_</tt>, <tt>get</tt>, and <tt>operator[]</code>
+    functions. */
+
+const Json::null_t Json::null;
+const Json Json::null_json;
+static const String array_string("[Array]", 7);
+static const String object_string("[Object]", 8);
+
+// Array internals
+
+Json::ArrayJson* Json::ArrayJson::make(int n) {
+    int cap = n < 8 ? 8 : n;
+    char* buf = new char[sizeof(ArrayJson) + cap * sizeof(Json)];
+    return new((void*) buf) ArrayJson(cap);
+}
+
+void Json::ArrayJson::destroy(ArrayJson* aj) {
+    if (aj)
+        for (int i = 0; i != aj->size; ++i)
+            aj->a[i].~Json();
+    delete[] reinterpret_cast<char*>(aj);
+}
+
+
+// Object internals
+
+Json::ObjectJson::ObjectJson(const ObjectJson &x)
+    : ComplexJson(), os_(x.os_), n_(x.n_), capacity_(x.capacity_),
+      hash_(x.hash_)
+{
+    size = x.size;
+    grow(true);
+}
+
+Json::ObjectJson::~ObjectJson()
+{
+    ObjectItem *ob = os_, *oe = ob + n_;
+    for (; ob != oe; ++ob)
+        if (ob->next_ > -2)
+            ob->~ObjectItem();
+    delete[] reinterpret_cast<char *>(os_);
+}
+
+void Json::ObjectJson::grow(bool copy)
+{
+    if (copy && !capacity_)
+        return;
+    int new_capacity;
+    if (copy)
+        new_capacity = capacity_;
+    else if (capacity_)
+        new_capacity = capacity_ * 2;
+    else
+        new_capacity = 8;
+    ObjectItem *new_os = reinterpret_cast<ObjectItem *>(operator new[](sizeof(ObjectItem) * new_capacity));
+    ObjectItem *ob = os_, *oe = ob + n_;
+    for (ObjectItem *oi = new_os; ob != oe; ++oi, ++ob) {
+        if (ob->next_ == -2)
+            oi->next_ = -2;
+        else if (copy)
+            new((void*) oi) ObjectItem(ob->v_.first, ob->v_.second, ob->next_);
+        else
+            memcpy(oi, ob, sizeof(ObjectItem));
+    }
+    if (!copy)
+        operator delete[](reinterpret_cast<void *>(os_));
+    os_ = new_os;
+    capacity_ = new_capacity;
+}
+
+void Json::ObjectJson::rehash()
+{
+    hash_.assign(hash_.size() * 2, -1);
+    for (int i = n_ - 1; i >= 0; --i) {
+        ObjectItem &oi = item(i);
+        if (oi.next_ > -2) {
+            int b = bucket(oi.v_.first.data(), oi.v_.first.length());
+            oi.next_ = hash_[b];
+            hash_[b] = i;
+        }
+    }
+}
+
+int Json::ObjectJson::find_insert(const String &key, const Json &value)
+{
+    if (hash_.empty())
+        hash_.assign(8, -1);
+    int *b = &hash_[bucket(key.data(), key.length())], chain = 0;
+    while (*b >= 0 && os_[*b].v_.first != key) {
+        b = &os_[*b].next_;
+        ++chain;
+    }
+    if (*b >= 0)
+        return *b;
+    else {
+        *b = n_;
+        if (n_ == capacity_)
+            grow(false);
+        // NB 'b' is invalid now
+        new ((void *) &os_[n_]) ObjectItem(key, value, -1);
+        ++n_;
+        ++size;
+        if (chain > 4)
+            rehash();
+        return n_ - 1;
+    }
+}
+
+Json &Json::ObjectJson::get_insert(Str key)
+{
+    if (hash_.empty())
+        hash_.assign(8, -1);
+    int *b = &hash_[bucket(key.data(), key.length())], chain = 0;
+    while (*b >= 0 && os_[*b].v_.first != key) {
+        b = &os_[*b].next_;
+        ++chain;
+    }
+    if (*b >= 0)
+        return os_[*b].v_.second;
+    else {
+        *b = n_;
+        if (n_ == capacity_)
+            grow(false);
+        // NB 'b' is invalid now
+        new ((void *) &os_[n_]) ObjectItem(String(key.data(), key.length()), null_json, -1);
+        ++n_;
+        ++size;
+        if (chain > 4)
+            rehash();
+        return os_[n_ - 1].v_.second;
+    }
+}
+
+void Json::ObjectJson::erase(int p) {
+    const ObjectItem& oi = item(p);
+    int* b = &hash_[bucket(oi.v_.first.data(), oi.v_.first.length())];
+    while (*b >= 0 && *b != p)
+        b = &os_[*b].next_;
+    assert(*b == p);
+    *b = os_[p].next_;
+    os_[p].~ObjectItem();
+    os_[p].next_ = -2;
+    --size;
+}
+
+Json::size_type Json::ObjectJson::erase(Str key) {
+    int* b = &hash_[bucket(key.data(), key.length())];
+    while (*b >= 0 && os_[*b].v_.first != key)
+        b = &os_[*b].next_;
+    if (*b >= 0) {
+        int p = *b;
+        *b = os_[p].next_;
+        os_[p].~ObjectItem();
+        os_[p].next_ = -2;
+        --size;
+        return 1;
+    } else
+        return 0;
+}
+
+namespace {
+template <typename T> bool string_to_int_key(const char *first,
+                                             const char *last, T& x)
+{
+    if (first == last || !isdigit((unsigned char) *first)
+        || (first[0] == '0' && first + 1 != last))
+        return false;
+    // XXX integer overflow
+    x = *first - '0';
+    for (++first; first != last && isdigit((unsigned char) *first); ++first)
+        x = 10 * x + *first - '0';
+    return first == last;
+}
+}
+
+void Json::hard_uniqueify_array(bool convert, int ncap_in) {
+    if (!convert)
+        precondition(is_null() || is_array());
+
+    rep_type old_u = u_;
+
+    unsigned ncap = std::max(ncap_in, 8);
+    if (old_u.x.type == j_array && old_u.a.x)
+        ncap = std::max(ncap, unsigned(old_u.a.x->size));
+    // New capacity: Round up to a power of 2, up to multiples of 1<<14.
+    unsigned xcap = iceil_log2(ncap);
+    if (xcap <= (1U << 14))
+        ncap = xcap;
+    else
+        ncap = ((ncap - 1) | ((1U << 14) - 1)) + 1;
+    u_.a.x = ArrayJson::make(ncap);
+    u_.a.type = j_array;
+
+    if (old_u.x.type == j_array && old_u.a.x && old_u.a.x->refcount == 1) {
+        u_.a.x->size = old_u.a.x->size;
+        memcpy(u_.a.x->a, old_u.a.x->a, sizeof(Json) * u_.a.x->size);
+        delete[] reinterpret_cast<char*>(old_u.a.x);
+    } else if (old_u.x.type == j_array && old_u.a.x) {
+        u_.a.x->size = old_u.a.x->size;
+        Json* last = u_.a.x->a + u_.a.x->size;
+        for (Json* it = u_.a.x->a, *oit = old_u.a.x->a; it != last; ++it, ++oit)
+            new((void*) it) Json(*oit);
+        old_u.a.x->deref(j_array);
+    } else if (old_u.x.type == j_object && old_u.o.x) {
+        ObjectItem *ob = old_u.o.x->os_, *oe = ob + old_u.o.x->n_;
+        unsigned i;
+        for (; ob != oe; ++ob)
+            if (ob->next_ > -2
+                && string_to_int_key(ob->v_.first.begin(),
+                                     ob->v_.first.end(), i)) {
+                if (i >= unsigned(u_.a.x->capacity))
+                    hard_uniqueify_array(false, i + 1);
+                if (i >= unsigned(u_.a.x->size)) {
+                    memset(&u_.a.x->a[u_.a.x->size], 0, sizeof(Json) * (i + 1 - u_.a.x->size));
+                    u_.a.x->size = i + 1;
+                }
+                u_.a.x->a[i] = ob->v_.second;
+            }
+        old_u.o.x->deref(j_object);
+    } else if (old_u.x.type < 0)
+        old_u.str.deref();
+}
+
+void Json::hard_uniqueify_object(bool convert) {
+    if (!convert)
+        precondition(is_null() || is_object());
+    ObjectJson* noj;
+    if (u_.x.type == j_object && u_.o.x) {
+        noj = new ObjectJson(*u_.o.x);
+        u_.o.x->deref(j_object);
+    } else if (u_.x.type == j_array && u_.a.x) {
+        noj = new ObjectJson;
+        for (int i = 0; i != u_.a.x->size; ++i)
+            noj->find_insert(String(i), u_.a.x->a[i]);
+        u_.a.x->deref(j_array);
+    } else {
+        noj = new ObjectJson;
+        if (u_.x.type < 0)
+            u_.str.deref();
+    }
+    u_.o.x = noj;
+    u_.o.type = j_object;
+}
+
+void Json::clear() {
+    static_assert(offsetof(rep_type, i.type) == offsetof(rep_type, x.type), "odd Json::rep_type.i.type offset");
+    static_assert(offsetof(rep_type, u.type) == offsetof(rep_type, x.type), "odd Json::rep_type.u.type offset");
+    static_assert(offsetof(rep_type, d.type) == offsetof(rep_type, x.type), "odd Json::rep_type.d.type offset");
+    static_assert(offsetof(rep_type, str.memo_offset) == offsetof(rep_type, x.type), "odd Json::rep_type.str.memo_offset offset");
+    static_assert(offsetof(rep_type, a.type) == offsetof(rep_type, x.type), "odd Json::rep_type.a.type offset");
+    static_assert(offsetof(rep_type, o.type) == offsetof(rep_type, x.type), "odd Json::rep_type.o.type offset");
+
+    if (u_.x.type == j_array) {
+        if (u_.a.x && u_.a.x->refcount == 1) {
+            Json* last = u_.a.x->a + u_.a.x->size;
+            for (Json* it = u_.a.x->a; it != last; ++it)
+                it->~Json();
+            u_.a.x->size = 0;
+        } else if (u_.a.x) {
+            u_.a.x->deref(j_array);
+            u_.a.x = 0;
+        }
+    } else if (u_.x.type == j_object) {
+        if (u_.o.x && u_.o.x->refcount == 1) {
+            ObjectItem* last = u_.o.x->os_ + u_.o.x->n_;
+            for (ObjectItem* it = u_.o.x->os_; it != last; ++it)
+                if (it->next_ != -2)
+                    it->~ObjectItem();
+            u_.o.x->n_ = u_.o.x->size = 0;
+            u_.o.x->hash_.assign(u_.o.x->hash_.size(), -1);
+        } else if (u_.o.x) {
+            u_.o.x->deref(j_object);
+            u_.o.x = 0;
+        }
+    } else {
+        if (u_.x.type < 0)
+            u_.str.deref();
+        memset(&u_, 0, sizeof(u_));
+    }
+}
+
+void* Json::uniqueify_array_insert(bool convert, size_type pos) {
+    size_type size = u_.a.x ? u_.a.x->size : 0;
+    uniqueify_array(convert, size + 1);
+    if (pos == (size_type) -1)
+        pos = size;
+    precondition(pos >= 0 && pos <= size);
+    if (pos != size)
+        memmove(&u_.a.x->a[pos + 1], &u_.a.x->a[pos], (size - pos) * sizeof(Json));
+    ++u_.a.x->size;
+    return (void*) &u_.a.x->a[pos];
+}
+
+Json::array_iterator Json::erase(array_iterator first, array_iterator last) {
+    if (first < last) {
+        uniqueify_array(false, 0);
+        size_type fpos = first - abegin();
+        size_type lpos = last - abegin();
+        size_type size = u_.a.x->size;
+        for (size_type pos = fpos; pos != lpos; ++pos)
+            u_.a.x->a[pos].~Json();
+        if (lpos != size)
+            memmove(&u_.a.x->a[fpos], &u_.a.x->a[lpos],
+                    (size - lpos) * sizeof(Json));
+        u_.a.x->size -= lpos - fpos;
+    }
+    return first;
+}
+
+/** @brief Reserve the array Json to hold at least @a n items. */
+void Json::reserve(size_type n) {
+    uniqueify_array(false, n);
+}
+
+/** @brief Resize the array Json to size @a n. */
+void Json::resize(size_type n) {
+    uniqueify_array(false, n);
+    while (u_.a.x->size > n && u_.a.x->size > 0) {
+        --u_.a.x->size;
+        u_.a.x->a[u_.a.x->size].~Json();
+    }
+    while (u_.a.x->size < n)
+        push_back(Json());
+}
+
+
+// Primitives
+
+int64_t Json::hard_to_i() const {
+    switch (u_.x.type) {
+    case j_array:
+    case j_object:
+        return size();
+    case j_bool:
+    case j_int:
+        return u_.i.x;
+    case j_unsigned:
+        return u_.u.x;
+    case j_double:
+        return int64_t(u_.d.x);
+    case j_null:
+    case j_string:
+    default:
+        if (!u_.x.x)
+            return 0;
+        invariant(u_.x.type <= 0);
+        const char *b = reinterpret_cast<const String&>(u_.str).c_str();
+        char *s;
+#if SIZEOF_LONG >= 8
+        long x = strtol(b, &s, 0);
+#else
+        long long x = strtoll(b, &s, 0);
+#endif
+        if (s == b + u_.str.length)
+            return x;
+        else
+            return (long) strtod(b, 0);
+    }
+}
+
+uint64_t Json::hard_to_u() const {
+    switch (u_.x.type) {
+    case j_array:
+    case j_object:
+        return size();
+    case j_bool:
+    case j_int:
+        return u_.i.x;
+    case j_unsigned:
+        return u_.u.x;
+    case j_double:
+        return uint64_t(u_.d.x);
+    case j_null:
+    case j_string:
+    default:
+        if (!u_.x.x)
+            return 0;
+        const char* b = reinterpret_cast<const String&>(u_.str).c_str();
+        char *s;
+#if SIZEOF_LONG >= 8
+        unsigned long x = strtoul(b, &s, 0);
+#else
+        unsigned long long x = strtoull(b, &s, 0);
+#endif
+        if (s == b + u_.str.length)
+            return x;
+        else
+            return (uint64_t) strtod(b, 0);
+    }
+}
+
+double Json::hard_to_d() const {
+    switch (u_.x.type) {
+    case j_array:
+    case j_object:
+        return size();
+    case j_bool:
+    case j_int:
+        return u_.i.x;
+    case j_unsigned:
+        return u_.u.x;
+    case j_double:
+        return u_.d.x;
+    case j_null:
+    case j_string:
+    default:
+        if (!u_.x.x)
+            return 0;
+        else
+            return strtod(reinterpret_cast<const String&>(u_.str).c_str(), 0);
+    }
+}
+
+bool Json::hard_to_b() const {
+    switch (u_.x.type) {
+    case j_array:
+    case j_object:
+        return !empty();
+    case j_bool:
+    case j_int:
+    case j_unsigned:
+        return u_.i.x != 0;
+    case j_double:
+        return u_.d.x;
+    case j_null:
+    case j_string:
+    default:
+        return u_.str.length != 0;
+    }
+}
+
+String Json::hard_to_s() const {
+    switch (u_.x.type) {
+    case j_array:
+        return array_string;
+    case j_object:
+        return object_string;
+    case j_bool:
+        return String(bool(u_.i.x));
+    case j_int:
+        return String(u_.i.x);
+    case j_unsigned:
+        return String(u_.u.x);
+    case j_double:
+        return String(u_.d.x);
+    case j_null:
+    case j_string:
+    default:
+        if (!u_.x.x)
+            return String::make_empty();
+        else
+            return String(u_.str);
+    }
+}
+
+const Json& Json::hard_get(Str key) const {
+    ArrayJson *aj;
+    unsigned i;
+    if (is_array() && (aj = ajson())
+        && string_to_int_key(key.begin(), key.end(), i)
+        && i < unsigned(aj->size))
+        return aj->a[i];
+    else
+        return make_null();
+}
+
+const Json& Json::hard_get(size_type x) const {
+    if (is_object() && u_.o.x)
+        return get(String(x));
+    else
+        return make_null();
+}
+
+Json& Json::hard_get_insert(size_type x) {
+    if (is_object())
+        return get_insert(String(x));
+    else {
+        uniqueify_array(true, x + 1);
+        if (u_.a.x->size <= x) {
+            memset(&u_.a.x->a[u_.a.x->size], 0, sizeof(Json) * (x + 1 - u_.a.x->size));
+            u_.a.x->size = x + 1;
+        }
+        return u_.a.x->a[x];
+    }
+}
+
+bool operator==(const Json& a, const Json& b) {
+    if ((a.u_.x.type > 0 || b.u_.x.type > 0)
+        && a.u_.x.type != b.u_.x.type)
+        return a.u_.u.x == b.u_.u.x
+            && a.u_.i.x >= 0
+            && a.is_int()
+            && b.is_int();
+    else if (a.u_.x.type == Json::j_int
+             || a.u_.x.type == Json::j_unsigned
+             || a.u_.x.type == Json::j_bool)
+        return a.u_.u.x == b.u_.u.x;
+    else if (a.u_.x.type == Json::j_double)
+        return a.u_.d.x == b.u_.d.x;
+    else if (a.u_.x.type > 0 || !a.u_.x.x || !b.u_.x.x)
+        return a.u_.x.x == b.u_.x.x;
+    else
+        return String(a.u_.str) == String(b.u_.str);
+}
+
+
+// Unparsing
+
+Json::unparse_manipulator Json::default_manipulator;
+
+bool Json::unparse_is_complex() const {
+    if (is_object()) {
+        if (ObjectJson *oj = ojson()) {
+            if (oj->size > 5)
+                return true;
+            ObjectItem *ob = oj->os_, *oe = ob + oj->n_;
+            for (; ob != oe; ++ob)
+                if (ob->next_ > -2 && !ob->v_.second.empty() && !ob->v_.second.is_primitive())
+                    return true;
+        }
+    } else if (is_array()) {
+        if (ArrayJson *aj = ajson()) {
+            if (aj->size > 8)
+                return true;
+            for (Json* it = aj->a; it != aj->a + aj->size; ++it)
+                if (!it->empty() && !it->is_primitive())
+                    return true;
+        }
+    }
+    return false;
+}
+
+void Json::unparse_indent(StringAccum &sa, const unparse_manipulator &m, int depth)
+{
+    sa << '\n';
+    depth *= (m.tab_width() ? m.tab_width() : 8);
+    sa.append_fill('\t', depth / 8);
+    sa.append_fill(' ', depth % 8);
+}
+
+namespace {
+const char* const upx_normal[] = {":", ","};
+const char* const upx_expanded[] = {": ", ","};
+const char* const upx_separated[] = {": ", ", "};
+}
+
+void Json::hard_unparse(StringAccum &sa, const unparse_manipulator &m, int depth) const
+{
+    if (is_object() || is_array()) {
+        bool expanded = depth < m.indent_depth() && unparse_is_complex();
+        const char* const* upx;
+        if (expanded)
+            upx = upx_expanded;
+        else if (m.space_separator())
+            upx = upx_separated;
+        else
+            upx = upx_normal;
+
+        if (is_object() && !u_.x.x)
+            sa << "{}";
+        else if (is_object()) {
+            sa << '{';
+            bool rest = false;
+            ObjectJson *oj = ojson();
+            ObjectItem *ob = oj->os_, *oe = ob + oj->n_;
+            for (; ob != oe; ++ob)
+                if (ob->next_ > -2) {
+                    if (rest)
+                        sa << upx[1];
+                    if (expanded)
+                        unparse_indent(sa, m, depth + 1);
+                    sa << '\"';
+                    ob->v_.first.encode_json(sa);
+                    sa << '\"' << upx[0];
+                    ob->v_.second.hard_unparse(sa, m, depth + 1);
+                    rest = true;
+                }
+            if (expanded)
+                unparse_indent(sa, m, depth);
+            sa << '}';
+        } else if (!u_.x.x)
+            sa << "[]";
+        else {
+            sa << '[';
+            bool rest = false;
+            ArrayJson* aj = ajson();
+            for (Json* it = aj->a; it != aj->a + aj->size; ++it) {
+                if (rest)
+                    sa << upx[1];
+                if (expanded)
+                    unparse_indent(sa, m, depth + 1);
+                it->hard_unparse(sa, m, depth + 1);
+                rest = true;
+            }
+            if (expanded)
+                unparse_indent(sa, m, depth);
+            sa << ']';
+        }
+    } else if (u_.x.type == j_null && !u_.x.x)
+        sa.append("null", 4);
+    else if (u_.x.type <= 0) {
+        sa << '\"';
+        reinterpret_cast<const String&>(u_.str).encode_json(sa);
+        sa << '\"';
+    } else if (u_.x.type == j_bool) {
+        bool b = u_.i.x;
+        sa.append(&"false\0true"[-b & 6], 5 - b);
+    } else if (u_.x.type == j_int)
+        sa << u_.i.x;
+    else if (u_.x.type == j_unsigned)
+        sa << u_.u.x;
+    else if (u_.x.type == j_double)
+        sa << u_.d.x;
+
+    if (depth == 0 && m.newline_terminator())
+        sa << '\n';
+}
+
+
+bool
+Json::assign_parse(const char* first, const char* last, const String& str)
+{
+    using std::swap;
+    Json::streaming_parser jsp;
+    first = jsp.consume(first, last, str, true);
+    if (first != last && (*first == ' ' || *first == '\n' || *first == '\r'
+                          || *first == '\t'))
+        ++first;
+    if (first == last && jsp.success()) {
+        swap(jsp.result(), *this);
+        return true;
+    } else
+        return false;
+}
+
+static inline bool in_range(uint8_t x, unsigned low, unsigned high) {
+    return (unsigned) x - low < high - low;
+}
+
+static inline bool in_range(int x, unsigned low, unsigned high) {
+    return (unsigned) x - low < high - low;
+}
+
+inline const uint8_t* Json::streaming_parser::error_at(const uint8_t* here) {
+    state_ = st_error;
+    return here;
+}
+
+inline Json* Json::streaming_parser::current() {
+    return stack_.empty() ? &json_ : stack_.back();
+}
+
+const uint8_t*
+Json::streaming_parser::consume(const uint8_t* first,
+                                const uint8_t* last,
+                                const String& str,
+                                bool complete) {
+    using std::swap;
+    Json j;
+
+    if (state_ >= 0 && (state_ & st_partmask)) {
+        if ((state_ & st_stringpart) && state_ >= st_object_colon)
+            goto string_object_key;
+        else if (state_ & st_stringpart)
+            goto string_value;
+        else if (state_ & st_primitivepart)
+            goto primitive;
+        else
+            goto number;
+    }
+
+    while (state_ >= 0 && first != last)
+        switch (*first) {
+        case ' ':
+        case '\n':
+        case '\r':
+        case '\t':
+            while (first != last && *first <= 32
+                   && (*first == ' ' || *first == '\n' || *first == '\r'
+                       || *first == '\t'))
+                ++first;
+            break;
+
+        case ',':
+            if (state_ == st_array_delim)
+                state_ = st_array_value;
+            else if (state_ == st_object_delim)
+                state_ = st_object_key;
+            else
+                goto error_here;
+            ++first;
+            break;
+
+        case ':':
+            if (state_ == st_object_colon) {
+                state_ = st_object_value;
+                ++first;
+                break;
+            } else
+                goto error_here;
+
+        case '{':
+            if (state_ <= st_object_value) {
+                if (stack_.size() == max_depth)
+                    goto error_here;
+                ++first;
+                if (state_ == st_initial && json_.is_o()) {
+                    swap(j, json_);
+                    j.clear();
+                } else
+                    j = Json::make_object();
+                goto value;
+            } else
+                goto error_here;
+
+        case '}':
+            if (state_ == st_object_initial || state_ == st_object_delim) {
+                ++first;
+                goto close_value;
+            } else
+                goto error_here;
+
+        case '[':
+            if (state_ <= st_object_value) {
+                if (stack_.size() == max_depth)
+                    goto error_here;
+                ++first;
+                if (state_ == st_initial && json_.is_a()) {
+                    swap(j, json_);
+                    j.clear();
+                } else
+                    j = Json::make_array();
+                goto value;
+            } else
+                goto error_here;
+
+        case ']':
+            if (state_ == st_array_initial || state_ == st_array_delim) {
+                ++first;
+                goto close_value;
+            } else
+                goto error_here;
+
+        case '\"':
+            if (state_ <= st_object_value) {
+                str_ = String();
+                ++first;
+            string_value:
+                first = consume_string(first, last, str);
+                if (state_ >= 0 && !(state_ & st_stringpart)) {
+                    j = Json(std::move(str_));
+                    goto value;
+                }
+            } else if (state_ == st_object_initial || state_ == st_object_key) {
+                state_ = st_object_colon;
+                str_ = String();
+                ++first;
+            string_object_key:
+                first = consume_string(first, last, str);
+                if (state_ >= 0 && !(state_ & st_stringpart)) {
+                    stack_.push_back(&current()->get_insert(std::move(str_)));
+                    continue;
+                }
+            } else
+                goto error_here;
+            break;
+
+        case 'n':
+        case 'f':
+        case 't':
+            if (state_ <= st_object_value) {
+            primitive:
+                first = consume_primitive(first, last, j);
+                if (state_ >= 0 && !(state_ & st_primitivepart))
+                    goto value;
+            } else
+                goto error_here;
+            break;
+
+        case '-':
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (state_ <= st_object_value) {
+            number:
+                first = consume_number(first, last, str, complete, j);
+                if (state_ >= 0 && !(state_ & st_numberpart))
+                    goto value;
+            } else
+                goto error_here;
+            break;
+
+        default:
+        error_here:
+            return error_at(first);
+
+        value: {
+            Json* jp = current();
+            if (state_ != st_initial && jp->is_a()) {
+                jp->push_back(std::move(j));
+                jp = &jp->back();
+            } else
+                swap(*jp, j);
+
+            if (state_ == st_object_value)
+                stack_.pop_back();
+            if (jp->is_a() || jp->is_o()) {
+                if (state_ != st_initial)
+                    stack_.push_back(jp);
+                state_ = jp->is_a() ? st_array_initial : st_object_initial;
+            } else if (state_ == st_object_value)
+                state_ = st_object_delim;
+            else if (state_ == st_array_initial || state_ == st_array_value)
+                state_ = st_array_delim;
+            else {
+                state_ = st_final;
+                return first;
+            }
+            break;
+        }
+
+        close_value:
+            if (stack_.empty()) {
+                state_ = st_final;
+                return first;
+            } else {
+                stack_.pop_back();
+                state_ = current()->is_a() ? st_array_delim : st_object_delim;
+            }
+            break;
+        }
+
+    return first;
+}
+
+const uint8_t*
+Json::streaming_parser::consume_string(const uint8_t* first,
+                                       const uint8_t* last,
+                                       const String& str) {
+    StringAccum sa = StringAccum::make_transfer(str_);
+
+    if (unlikely(state_ & st_partlenmask)) {
+        first = consume_stringpart(sa, first, last);
+        if (state_ == st_error)
+            return first;
+    }
+
+    const uint8_t* prev = first;
+    while (first != last) {
+        if (likely(in_range(*first, 32, 128)) && *first != '\\' && *first != '\"')
+            ++first;
+        else if (*first == '\\') {
+            sa.append(prev, first);
+            prev = first = consume_backslash(sa, first, last);
+            if (state_ == st_error)
+                return first;
+        } else if (*first == '\"')
+            break;
+        else if (unlikely(!in_range(*first, 0xC2, 0xF5)))
+            return error_at(first);
+        else {
+            int want = 2 + (*first >= 0xE0) + (*first >= 0xF0);
+            int n = last - first < want ? last - first : want;
+            if ((n > 1 && unlikely(!in_range(first[1], 0x80, 0xC0)))
+                || (*first >= 0xE0
+                    && ((*first == 0xE0 && first[1] < 0xA0) /* overlong */
+                        || (*first == 0xED && first[1] >= 0xA0) /* surrogate */
+                        || (*first == 0xF0 && first[1] < 0x90) /* overlong */
+                        || (*first == 0xF4 && first[1] >= 0x90) /* not a char */
+                        )))
+                return error_at(first + 1);
+            else if (n > 2 && unlikely(!in_range(first[2], 0x80, 0xC0)))
+                return error_at(first + 2);
+            else if (n > 3 && unlikely(!in_range(first[3], 0x80, 0xC0)))
+                return error_at(first + 3);
+            first += n;
+            if (n != want) {
+                state_ |= n;
+                break;
+            }
+        }
+    }
+
+    if (!sa.empty() || first == last)
+        sa.append(prev, first);
+    if (first != last) {
+        if (!sa.empty())
+            str_ = sa.take_string();
+        else if (prev >= str.ubegin() && first <= str.uend())
+            str_ = str.fast_substring(prev, first);
+        else
+            str_ = String(prev, first);
+        state_ &= ~st_stringpart;
+        return first + 1;
+    } else {
+        state_ |= st_stringpart;
+        str_ = sa.take_string();
+        return first;
+    }
+}
+
+const uint8_t*
+Json::streaming_parser::consume_stringpart(StringAccum& sa,
+                                           const uint8_t* first,
+                                           const uint8_t* last) {
+    while ((state_ & st_partlenmask) && first != last) {
+        int part = state_ & st_partlenmask;
+        uint8_t tag = sa[sa.length() - part];
+        if ((tag != '\\' && (*first & 0xC0) != 0x80)
+            || (tag == '\\' && part == 6 && *first != '\\')
+            || (tag == '\\' && part == 7 && *first != 'u')
+            || (tag == '\\' && part != 1 && part != 6 && part != 7
+                && !isxdigit(*first))) {
+            state_ = st_error;
+            break;
+        }
+        sa.append(*first);
+        if ((tag != '\\' && part == 1 + (tag >= 0xE0) + (tag >= 0xF0))
+            || (tag == '\\' && (part == 1 || part == 5 || part == 11))) {
+            uint8_t buf[12];
+            memcpy(buf, sa.end() - part - 1, part + 1);
+            sa.adjust_length(-part - 1);
+            state_ -= st_stringpart | part;
+            str_ = sa.take_string();
+            (void) consume_string(buf, buf + part + 1, String());
+            if (state_ == st_error)
+                break;
+            sa = StringAccum::make_transfer(str_);
+        } else
+            ++state_;
+        ++first;
+    }
+    return first;
+}
+
+const uint8_t*
+Json::streaming_parser::consume_backslash(StringAccum& sa,
+                                          const uint8_t* first,
+                                          const uint8_t* last) {
+    const uint8_t* prev = first;
+    int ch = 0;
+
+    if (first + 1 == last)
+        goto incomplete;
+    else if (first[1] == '\"' || first[1] == '\\' || first[1] == '/')
+        ch = first[1];
+    else if (first[1] == 'b')
+        ch = '\b';
+    else if (first[1] == 'f')
+        ch = '\f';
+    else if (first[1] == 'n')
+        ch = '\n';
+    else if (first[1] == 'r')
+        ch = '\r';
+    else if (first[1] == 't')
+        ch = '\t';
+    else if (first[1] == 'u') {
+        for (int i = 2; i < 6; ++i) {
+            if (first + i == last)
+                goto incomplete;
+            else if (in_range(first[i], '0', '9' + 1))
+                ch = 16 * ch + first[i] - '0';
+            else if (in_range(first[i], 'A', 'F' + 1))
+                ch = 16 * ch + first[i] - 'A' + 10;
+            else if (in_range(first[i], 'a', 'f' + 1))
+                ch = 16 * ch + first[i] - 'a' + 10;
+            else
+                return error_at(&first[i]);
+        }
+        first += 4;
+        // special handling required for surrogate pairs
+        if (unlikely(in_range(ch, 0xD800, 0xE000))) {
+            if (ch >= 0xDC00)
+                return error_at(&first[1]);
+            else if (first + 2 == last)
+                goto incomplete;
+            else if (first[2] != '\\')
+                return error_at(&first[2]);
+            else if (first + 3 == last)
+                goto incomplete;
+            else if (first[3] != 'u')
+                return error_at(&first[3]);
+            int ch2 = 0;
+            for (int i = 4; i < 8; ++i) {
+                if (first + i == last)
+                    goto incomplete;
+                else if (in_range(first[i], '0', '9' + 1))
+                    ch2 = 16 * ch2 + first[i] - '0';
+                else if (in_range(first[i], 'A', 'F' + 1))
+                    ch2 = 16 * ch2 + first[i] - 'A' + 10;
+                else if (in_range(first[i], 'A', 'F' + 1))
+                    ch2 = 16 * ch2 + first[i] - 'a' + 10;
+                else
+                    return error_at(&first[i]);
+            }
+            if (!in_range(ch2, 0xDC00, 0xE000))
+                return error_at(&first[7]);
+            ch = 0x10000 + (ch - 0xD800) * 0x400 + (ch2 - 0xDC00);
+            first += 6;
+        }
+    }
+
+    if (!ch || !sa.append_utf8(ch))
+        return error_at(&first[1]);
+    return first + 2;
+
+ incomplete:
+    state_ |= last - prev;
+    sa.append(prev, last);
+    return last;
+}
+
+const uint8_t*
+Json::streaming_parser::consume_primitive(const uint8_t* first,
+                                          const uint8_t* last,
+                                          Json& j) {
+    const char* t = "null\0false\0true";
+    int n;
+    if (unlikely(state_ & st_primitivepart)) {
+        n = state_ & st_partlenmask;
+        state_ &= ~st_partmask;
+    } else {
+        n = (*first == 'n' ? 1 : (*first == 'f' ? 6 : 12));
+        ++first;
+    }
+
+    for (; first != last && t[n]; ++n, ++first)
+        if (t[n] != *first)
+            return error_at(first);
+
+    if (t[n])
+        state_ |= st_primitivepart | n;
+    else if (n == 4)
+        j = Json();
+    else if (n == 10)
+        j = Json(false);
+    else
+        j = Json(true);
+    return first;
+}
+
+const uint8_t*
+Json::streaming_parser::consume_number(const uint8_t* first,
+                                       const uint8_t* last,
+                                       const String& str,
+                                       bool complete,
+                                       Json& j) {
+    const uint8_t* prev = first;
+    int position = state_ & st_partlenmask;
+
+    switch (position) {
+    case 0:
+        if (*first == '-')
+            ++first;
+        /* fallthru */
+    case 2:
+        if (first != last && *first == '0') {
+            position = 3;
+            ++first;
+        } else if (first != last && in_range(*first, '1', '9' + 1)) {
+            position = 1;
+            ++first;
+        case 1:
+            while (first != last && in_range(*first, '0', '9' + 1))
+                ++first;
+        } else
+            position = 2;
+        /* fallthru */
+    case 3:
+        if (first != last && *first == '.') {
+            ++first;
+            goto decimal;
+        }
+    maybe_exponent:
+        if (first != last && (*first == 'e' || *first == 'E')) {
+            ++first;
+            goto exponent;
+        }
+        break;
+
+    decimal:
+    case 4:
+        position = 4;
+        if (first != last && in_range(*first, '0', '9' + 1)) {
+            ++first;
+            position = 5;
+        }
+        /* fallthru */
+    case 5:
+        while (first != last && in_range(*first, '0', '9' + 1))
+            ++first;
+        if (first != last && position == 5)
+            goto maybe_exponent;
+        break;
+
+    exponent:
+    case 6:
+        position = 6;
+        if (first != last && (*first == '+' || *first == '-'))
+            ++first;
+        else if (first == last)
+            break;
+        /* fallthru */
+    case 8:
+        position = 8;
+        if (first != last && in_range(*first, '0', '9' + 1)) {
+            ++first;
+            position = 9;
+        }
+        /* fallthru */
+    case 9:
+        while (first != last && in_range(*first, '0', '9' + 1))
+            ++first;
+        break;
+    }
+
+    if (first != last || complete) {
+        if (!(position & 1))
+            goto error_here;
+        last = first;
+        if (state_ & st_partlenmask) {
+            str_.append(prev, first);
+            prev = str_.ubegin();
+            first = str_.uend();
+        }
+        if (prev + 1 == first)
+            j = Json(int(*prev - '0'));
+        else if (position < 4) {
+            bool negative = *prev == '-';
+            prev += int(negative);
+            uint64_t x = 0;
+            while (prev != first) {
+                x = (x * 10) + *prev - '0';
+                ++prev;
+            }
+            if (negative)
+                j = Json(-int64_t(x));
+            else
+                j = Json(x);
+        } else {
+            if (!(state_ & st_partlenmask))
+                str_ = String(prev, first);
+            double x = strtod(str_.c_str(), 0);
+            j = Json(x);
+        }
+        state_ &= ~st_partmask;
+        str_ = String();
+        return last;
+    }
+
+    if (state_ & st_partmask)
+        str_.append(prev, first);
+    else if (prev >= str.ubegin() && first <= str.uend())
+        str_ = str.substring(prev, first);
+    else
+        str_ = String(prev, first);
+    state_ = (state_ & ~st_partmask) | st_numberpart | position;
+    return first;
+
+ error_here:
+    str_ = String();
+    return error_at(first);
+}
+
+} // namespace lcdf
diff --git a/silo/masstree/json.hh b/silo/masstree/json.hh
new file mode 100644 (file)
index 0000000..2c7700b
--- /dev/null
@@ -0,0 +1,3180 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+// -*- c-basic-offset: 4 -*-
+#ifndef JSON_HH
+#define JSON_HH
+#include "straccum.hh"
+#include "str.hh"
+#include <vector>
+#include <utility>
+#include <stdlib.h>
+namespace lcdf {
+
+template <typename P> class Json_proxy_base;
+template <typename T> class Json_object_proxy;
+template <typename T> class Json_object_str_proxy;
+template <typename T> class Json_array_proxy;
+class Json_get_proxy;
+
+template <typename T, size_t S = sizeof(String::rep_type) - sizeof(T)>
+struct Json_rep_item;
+template <typename T> struct Json_rep_item<T, 4> {
+    T x;
+    int type;
+};
+template <typename T> struct Json_rep_item<T, 8> {
+    T x;
+    int padding;
+    int type;
+};
+
+class Json {
+    enum json_type { // order matters
+        j_string = -1, j_null = 0,
+        j_array = 1, j_object = 2,
+        j_int = 3, j_unsigned = 4, j_double = 5, j_bool = 6
+    };
+
+  public:
+    struct null_t {
+        inline constexpr null_t() { }
+    };
+    static const null_t null;
+    static const Json null_json;
+
+    typedef int size_type;
+
+    typedef std::pair<const String, Json> object_value_type;
+    class object_iterator;
+    class const_object_iterator;
+
+    typedef Json array_value_type;
+    class array_iterator;
+    class const_array_iterator;
+
+    class iterator;
+    class const_iterator;
+
+    typedef bool (Json::*unspecified_bool_type)() const;
+    class unparse_manipulator;
+    class streaming_parser;
+
+    // Constructors
+    inline Json();
+    inline Json(const Json& x);
+    template <typename P> inline Json(const Json_proxy_base<P>& x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline Json(Json&& x);
+#endif
+    inline Json(const null_t& x);
+    inline Json(int x);
+    inline Json(unsigned x);
+    inline Json(long x);
+    inline Json(unsigned long x);
+    inline Json(long long x);
+    inline Json(unsigned long long x);
+    inline Json(double x);
+    inline Json(bool x);
+    inline Json(const String& x);
+    inline Json(Str x);
+    inline Json(const char* x);
+    template <typename T> inline Json(const std::vector<T>& x);
+    template <typename T> inline Json(T first, T last);
+    inline ~Json();
+
+    static inline const Json& make_null();
+    static inline Json make_array();
+    static inline Json make_array_reserve(int n);
+    template <typename... Args>
+    static inline Json array(Args&&... rest);
+    static inline Json make_object();
+    template <typename... Args>
+    static inline Json object(Args&&... rest);
+    static inline Json make_string(const String& x);
+    static inline Json make_string(const char* s, int len);
+
+    // Type information
+    inline bool truthy() const;
+    inline bool falsy() const;
+    inline operator unspecified_bool_type() const;
+    inline bool operator!() const;
+
+    inline bool is_null() const;
+    inline bool is_int() const;
+    inline bool is_i() const;
+    inline bool is_unsigned() const;
+    inline bool is_u() const;
+    inline bool is_signed() const;
+    inline bool is_nonnegint() const;
+    inline bool is_double() const;
+    inline bool is_d() const;
+    inline bool is_number() const;
+    inline bool is_n() const;
+    inline bool is_bool() const;
+    inline bool is_b() const;
+    inline bool is_string() const;
+    inline bool is_s() const;
+    inline bool is_array() const;
+    inline bool is_a() const;
+    inline bool is_object() const;
+    inline bool is_o() const;
+    inline bool is_primitive() const;
+
+    inline bool empty() const;
+    inline size_type size() const;
+    inline bool shared() const;
+
+    void clear();
+
+    // Primitive extractors
+    inline int64_t to_i() const;
+    inline uint64_t to_u() const;
+    inline uint64_t to_u64() const;
+    inline bool to_i(int& x) const;
+    inline bool to_i(unsigned& x) const;
+    inline bool to_i(long& x) const;
+    inline bool to_i(unsigned long& x) const;
+    inline bool to_i(long long& x) const;
+    inline bool to_i(unsigned long long& x) const;
+    inline int64_t as_i() const;
+    inline int64_t as_i(int64_t default_value) const;
+    inline uint64_t as_u() const;
+    inline uint64_t as_u(uint64_t default_value) const;
+
+    inline double to_d() const;
+    inline bool to_d(double& x) const;
+    inline double as_d() const;
+    inline double as_d(double default_value) const;
+
+    inline bool to_b() const;
+    inline bool to_b(bool& x) const;
+    inline bool as_b() const;
+    inline bool as_b(bool default_value) const;
+
+    inline String to_s() const;
+    inline bool to_s(Str& x) const;
+    inline bool to_s(String& x) const;
+    inline String& as_s();
+    inline const String& as_s() const;
+    inline const String& as_s(const String& default_value) const;
+
+    // Object methods
+    inline size_type count(Str key) const;
+    inline const Json& get(Str key) const;
+    inline Json& get_insert(const String& key);
+    inline Json& get_insert(Str key);
+    inline Json& get_insert(const char* key);
+
+    inline long get_i(Str key) const;
+    inline double get_d(Str key) const;
+    inline bool get_b(Str key) const;
+    inline String get_s(Str key) const;
+
+    inline const Json_get_proxy get(Str key, Json& x) const;
+    inline const Json_get_proxy get(Str key, int& x) const;
+    inline const Json_get_proxy get(Str key, unsigned& x) const;
+    inline const Json_get_proxy get(Str key, long& x) const;
+    inline const Json_get_proxy get(Str key, unsigned long& x) const;
+    inline const Json_get_proxy get(Str key, long long& x) const;
+    inline const Json_get_proxy get(Str key, unsigned long long& x) const;
+    inline const Json_get_proxy get(Str key, double& x) const;
+    inline const Json_get_proxy get(Str key, bool& x) const;
+    inline const Json_get_proxy get(Str key, Str& x) const;
+    inline const Json_get_proxy get(Str key, String& x) const;
+
+    const Json& operator[](Str key) const;
+    inline Json_object_proxy<Json> operator[](const String& key);
+    inline Json_object_str_proxy<Json> operator[](Str key);
+    inline Json_object_str_proxy<Json> operator[](const char* key);
+
+    inline const Json& at(Str key) const;
+    inline Json& at_insert(const String& key);
+    inline Json& at_insert(Str key);
+    inline Json& at_insert(const char* key);
+
+    inline Json& set(const String& key, Json value);
+    template <typename P>
+    inline Json& set(const String& key, const Json_proxy_base<P>& value);
+    inline Json& unset(Str key);
+
+    inline Json& set_list();
+    template <typename T, typename... Args>
+    inline Json& set_list(const String& key, T value, Args&&... rest);
+
+    inline std::pair<object_iterator, bool> insert(const object_value_type& x);
+    inline object_iterator insert(object_iterator position,
+                                  const object_value_type& x);
+    inline object_iterator erase(object_iterator it);
+    inline size_type erase(Str key);
+
+    inline Json& merge(const Json& x);
+    template <typename P> inline Json& merge(const Json_proxy_base<P>& x);
+
+    // Array methods
+    inline const Json& get(size_type x) const;
+    inline Json& get_insert(size_type x);
+
+    inline const Json& operator[](size_type x) const;
+    inline Json_array_proxy<Json> operator[](size_type x);
+
+    inline const Json& at(size_type x) const;
+    inline Json& at_insert(size_type x);
+
+    inline const Json& back() const;
+    inline Json& back();
+
+    inline Json& push_back(Json x);
+    template <typename P> inline Json& push_back(const Json_proxy_base<P>& x);
+    inline void pop_back();
+
+    inline Json& push_back_list();
+    template <typename T, typename... Args>
+    inline Json& push_back_list(T first, Args&&... rest);
+
+    inline array_iterator insert(array_iterator position, Json x);
+    template <typename P>
+    inline array_iterator insert(array_iterator position, const Json_proxy_base<P>& x);
+    array_iterator erase(array_iterator first, array_iterator last);
+    inline array_iterator erase(array_iterator position);
+
+    void reserve(size_type n);
+    void resize(size_type n);
+
+    inline Json* array_data();
+    inline const Json* array_data() const;
+    inline const Json* array_cdata() const;
+    inline Json* end_array_data();
+    inline const Json* end_array_data() const;
+    inline const Json* end_array_cdata() const;
+
+    // Iteration
+    inline const_object_iterator obegin() const;
+    inline const_object_iterator oend() const;
+    inline object_iterator obegin();
+    inline object_iterator oend();
+    inline const_object_iterator cobegin() const;
+    inline const_object_iterator coend() const;
+
+    inline const_array_iterator abegin() const;
+    inline const_array_iterator aend() const;
+    inline array_iterator abegin();
+    inline array_iterator aend();
+    inline const_array_iterator cabegin() const;
+    inline const_array_iterator caend() const;
+
+    inline const_iterator begin() const;
+    inline const_iterator end() const;
+    inline iterator begin();
+    inline iterator end();
+    inline const_iterator cbegin() const;
+    inline const_iterator cend() const;
+
+    // Unparsing
+    static inline unparse_manipulator indent_depth(int x);
+    static inline unparse_manipulator tab_width(int x);
+    static inline unparse_manipulator newline_terminator(bool x);
+    static inline unparse_manipulator space_separator(bool x);
+
+    inline String unparse() const;
+    inline String unparse(const unparse_manipulator& m) const;
+    inline void unparse(StringAccum& sa) const;
+    inline void unparse(StringAccum& sa, const unparse_manipulator& m) const;
+
+    // Parsing
+    inline bool assign_parse(const String& str);
+    inline bool assign_parse(const char* first, const char* last);
+
+    static inline Json parse(const String& str);
+    static inline Json parse(const char* first, const char* last);
+
+    // Assignment
+    inline Json& operator=(const Json& x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline Json& operator=(Json&& x);
+#endif
+    inline Json& operator=(int x);
+    inline Json& operator=(unsigned x);
+    inline Json& operator=(long x);
+    inline Json& operator=(unsigned long x);
+    inline Json& operator=(long long x);
+    inline Json& operator=(unsigned long long x);
+    inline Json& operator=(double x);
+    inline Json& operator=(bool x);
+    inline Json& operator=(const String& x);
+    template <typename P> inline Json& operator=(const Json_proxy_base<P>& x);
+
+    inline Json& operator++();
+    inline void operator++(int);
+    inline Json& operator--();
+    inline void operator--(int);
+    inline Json& operator+=(int x);
+    inline Json& operator+=(unsigned x);
+    inline Json& operator+=(long x);
+    inline Json& operator+=(unsigned long x);
+    inline Json& operator+=(long long x);
+    inline Json& operator+=(unsigned long long x);
+    inline Json& operator+=(double x);
+    inline Json& operator+=(const Json& x);
+    inline Json& operator-=(int x);
+    inline Json& operator-=(unsigned x);
+    inline Json& operator-=(long x);
+    inline Json& operator-=(unsigned long x);
+    inline Json& operator-=(long long x);
+    inline Json& operator-=(unsigned long long x);
+    inline Json& operator-=(double x);
+    inline Json& operator-=(const Json& x);
+
+    friend bool operator==(const Json& a, const Json& b);
+
+    inline void swap(Json& x);
+
+  private:
+    enum {
+        st_initial = 0, st_array_initial = 1, st_array_delim = 2,
+        st_array_value = 3, st_object_initial = 4, st_object_delim = 5,
+        st_object_key = 6, st_object_colon = 7, st_object_value = 8,
+        max_depth = 2048
+    };
+
+    struct ComplexJson {
+        int refcount;
+        int size;
+        ComplexJson()
+            : refcount(1) {
+        }
+        inline void ref();
+        inline void deref(json_type j);
+      private:
+        ComplexJson(const ComplexJson& x); // does not exist
+    };
+
+    struct ArrayJson;
+    struct ObjectItem;
+    struct ObjectJson;
+
+    union rep_type {
+        Json_rep_item<int64_t> i;
+        Json_rep_item<uint64_t> u;
+        Json_rep_item<double> d;
+        String::rep_type str;
+        Json_rep_item<ArrayJson*> a;
+        Json_rep_item<ObjectJson*> o;
+        Json_rep_item<ComplexJson*> x;
+    } u_;
+
+    inline void deref();
+
+    inline ObjectJson* ojson() const;
+    inline ArrayJson* ajson() const;
+
+    int64_t hard_to_i() const;
+    uint64_t hard_to_u() const;
+    double hard_to_d() const;
+    bool hard_to_b() const;
+    String hard_to_s() const;
+    inline void force_number();
+    inline void force_double();
+    inline Json& add(double x);
+    template <typename T> inline Json& add(T x);
+    inline Json& subtract(double x);
+    template <typename T> inline Json& subtract(T x);
+
+    const Json& hard_get(Str key) const;
+    const Json& hard_get(size_type x) const;
+    Json& hard_get_insert(size_type x);
+
+    inline void uniqueify_object(bool convert);
+    void hard_uniqueify_object(bool convert);
+    inline void uniqueify_array(bool convert, int ncap);
+    void hard_uniqueify_array(bool convert, int ncap);
+    void* uniqueify_array_insert(bool convert, size_type pos);
+
+    static unparse_manipulator default_manipulator;
+    bool unparse_is_complex() const;
+    static void unparse_indent(StringAccum &sa, const unparse_manipulator &m, int depth);
+    void hard_unparse(StringAccum &sa, const unparse_manipulator &m, int depth) const;
+
+    bool assign_parse(const char* first, const char* last, const String &str);
+
+    friend class object_iterator;
+    friend class const_object_iterator;
+    friend class array_iterator;
+    friend class const_array_iterator;
+    friend Json operator+(Json);
+    friend Json operator-(Json);
+};
+
+
+struct Json::ArrayJson : public ComplexJson {
+    int capacity;
+    Json a[0];
+
+    inline ArrayJson(int cap)
+        : capacity(cap) {
+        size = 0;
+    }
+    static ArrayJson* make(int n);
+    static void destroy(ArrayJson* a);
+};
+
+struct Json::ObjectItem {
+    std::pair<const String, Json> v_;
+    int next_;
+    explicit ObjectItem(const String &key, const Json& value, int next)
+        : v_(key, value), next_(next) {
+    }
+};
+
+struct Json::ObjectJson : public ComplexJson {
+    ObjectItem *os_;
+    int n_;
+    int capacity_;
+    std::vector<int> hash_;
+    ObjectJson()
+        : os_(), n_(0), capacity_(0) {
+        size = 0;
+    }
+    ObjectJson(const ObjectJson& x);
+    ~ObjectJson();
+    void grow(bool copy);
+    int bucket(const char* s, int len) const {
+        return String::hashcode(s, s + len) & (hash_.size() - 1);
+    }
+    ObjectItem& item(int p) const {
+        return os_[p];
+    }
+    int find(const char* s, int len) const {
+        if (hash_.size()) {
+            int p = hash_[bucket(s, len)];
+            while (p >= 0) {
+                ObjectItem &oi = item(p);
+                if (oi.v_.first.equals(s, len))
+                    return p;
+                p = oi.next_;
+            }
+        }
+        return -1;
+    }
+    int find_insert(const String& key, const Json& value);
+    inline Json& get_insert(const String& key) {
+        int p = find_insert(key, make_null());
+        return item(p).v_.second;
+    }
+    Json& get_insert(Str key);
+    void erase(int p);
+    size_type erase(Str key);
+    void rehash();
+};
+
+inline const Json& Json::make_null() {
+    return null_json;
+}
+
+inline void Json::ComplexJson::ref() {
+    if (refcount >= 0)
+        ++refcount;
+}
+
+inline void Json::ComplexJson::deref(json_type j) {
+    if (refcount >= 1 && --refcount == 0) {
+        if (j == j_object)
+            delete static_cast<ObjectJson*>(this);
+        else
+            ArrayJson::destroy(static_cast<ArrayJson*>(this));
+    }
+}
+
+inline Json::ArrayJson* Json::ajson() const {
+    precondition(u_.x.type == j_null || u_.x.type == j_array);
+    return u_.a.x;
+}
+
+inline Json::ObjectJson* Json::ojson() const {
+    precondition(u_.x.type == j_null || u_.x.type == j_object);
+    return u_.o.x;
+}
+
+inline void Json::uniqueify_array(bool convert, int ncap) {
+    if (u_.x.type != j_array || !u_.a.x || u_.a.x->refcount != 1
+        || (ncap > 0 && ncap > u_.a.x->capacity))
+        hard_uniqueify_array(convert, ncap);
+}
+
+inline void Json::uniqueify_object(bool convert) {
+    if (u_.x.type != j_object || !u_.o.x || u_.o.x->refcount != 1)
+        hard_uniqueify_object(convert);
+}
+
+
+class Json::const_object_iterator { public:
+    typedef std::pair<const String, Json> value_type;
+    typedef const value_type* pointer_type;
+    typedef const value_type& reference_type;
+    typedef std::forward_iterator_tag iterator_category;
+
+    const_object_iterator() {
+    }
+    typedef bool (const_object_iterator::*unspecified_bool_type)() const;
+    operator unspecified_bool_type() const {
+        return live() ? &const_object_iterator::live : 0;
+    }
+    bool live() const {
+        return i_ >= 0;
+    }
+    const value_type& operator*() const {
+        return j_->ojson()->item(i_).v_;
+    }
+    const value_type* operator->() const {
+        return &(**this);
+    }
+    const String& key() const {
+        return (**this).first;
+    }
+    const Json& value() const {
+        return (**this).second;
+    }
+    void operator++() {
+        ++i_;
+        fix();
+    }
+    void operator++(int) {
+        ++(*this);
+    }
+  private:
+    const Json* j_;
+    int i_;
+    const_object_iterator(const Json* j, int i)
+        : j_(j), i_(i) {
+        if (i_ >= 0)
+            fix();
+    }
+    void fix() {
+        ObjectJson* oj = j_->ojson();
+    retry:
+        if (!oj || i_ >= oj->n_)
+            i_ = -1;
+        else if (oj->item(i_).next_ == -2) {
+            ++i_;
+            goto retry;
+        }
+    }
+    friend class Json;
+    friend bool operator==(const const_object_iterator&, const const_object_iterator&);
+};
+
+class Json::object_iterator : public const_object_iterator { public:
+    typedef value_type* pointer_type;
+    typedef value_type& reference_type;
+
+    object_iterator() {
+    }
+    value_type& operator*() const {
+        const_cast<Json*>(j_)->uniqueify_object(false);
+        return j_->ojson()->item(i_).v_;
+    }
+    value_type* operator->() const {
+        return &(**this);
+    }
+    Json& value() const {
+        return (**this).second;
+    }
+  private:
+    object_iterator(Json* j, int i)
+        : const_object_iterator(j, i) {
+    }
+    friend class Json;
+};
+
+inline bool operator==(const Json::const_object_iterator& a, const Json::const_object_iterator& b) {
+    return a.j_ == b.j_ && a.i_ == b.i_;
+}
+
+inline bool operator!=(const Json::const_object_iterator& a, const Json::const_object_iterator& b) {
+    return !(a == b);
+}
+
+class Json::const_array_iterator { public:
+    typedef Json::size_type difference_type;
+    typedef Json value_type;
+    typedef const Json* pointer_type;
+    typedef const Json& reference_type;
+    typedef std::random_access_iterator_tag iterator_category;
+
+    const_array_iterator() {
+    }
+    typedef bool (const_array_iterator::*unspecified_bool_type)() const;
+    operator unspecified_bool_type() const {
+        return live() ? &const_array_iterator::live : 0;
+    }
+    bool live() const {
+        ArrayJson* aj = j_->ajson();
+        return aj && i_ < aj->size;
+    }
+    const Json& operator*() const {
+        return j_->ajson()->a[i_];
+    }
+    const Json& operator[](difference_type i) const {
+        return j_->ajson()->a[i_ + i];
+    }
+    const Json* operator->() const {
+        return &(**this);
+    }
+    const Json& value() const {
+        return **this;
+    }
+    void operator++(int) {
+        ++i_;
+    }
+    void operator++() {
+        ++i_;
+    }
+    void operator--(int) {
+        --i_;
+    }
+    void operator--() {
+        --i_;
+    }
+    const_array_iterator& operator+=(difference_type x) {
+        i_ += x;
+        return *this;
+    }
+    const_array_iterator& operator-=(difference_type x) {
+        i_ -= x;
+        return *this;
+    }
+  private:
+    const Json* j_;
+    int i_;
+    const_array_iterator(const Json* j, int i)
+        : j_(j), i_(i) {
+    }
+    friend class Json;
+    friend class Json::array_iterator;
+    friend bool operator==(const const_array_iterator&, const const_array_iterator&);
+    friend bool operator<(const const_array_iterator&, const const_array_iterator&);
+    friend difference_type operator-(const const_array_iterator&, const const_array_iterator&);
+};
+
+class Json::array_iterator : public const_array_iterator { public:
+    typedef const Json* pointer_type;
+    typedef const Json& reference_type;
+
+    array_iterator() {
+    }
+    Json& operator*() const {
+        const_cast<Json*>(j_)->uniqueify_array(false, 0);
+        return j_->ajson()->a[i_];
+    }
+    Json& operator[](difference_type i) const {
+        const_cast<Json*>(j_)->uniqueify_array(false, 0);
+        return j_->ajson()->a[i_ + i];
+    }
+    Json* operator->() const {
+        return &(**this);
+    }
+    Json& value() const {
+        return **this;
+    }
+    array_iterator& operator+=(difference_type x) {
+        i_ += x;
+        return *this;
+    }
+    array_iterator& operator-=(difference_type x) {
+        i_ -= x;
+        return *this;
+    }
+  private:
+    array_iterator(Json* j, int i)
+        : const_array_iterator(j, i) {
+    }
+    friend class Json;
+};
+
+inline bool operator==(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
+    return a.j_ == b.j_ && a.i_ == b.i_;
+}
+
+inline bool operator<(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
+    return a.j_ < b.j_ || (a.j_ == b.j_ && a.i_ < b.i_);
+}
+
+inline bool operator!=(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
+    return !(a == b);
+}
+
+inline bool operator<=(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
+    return !(b < a);
+}
+
+inline bool operator>(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
+    return b < a;
+}
+
+inline bool operator>=(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
+    return !(a < b);
+}
+
+inline Json::const_array_iterator operator+(Json::const_array_iterator a, Json::const_array_iterator::difference_type i) {
+    return a += i;
+}
+inline Json::array_iterator operator+(Json::array_iterator a, Json::array_iterator::difference_type i) {
+    return a += i;
+}
+
+inline Json::const_array_iterator operator-(Json::const_array_iterator a, Json::const_array_iterator::difference_type i) {
+    return a -= i;
+}
+inline Json::array_iterator operator-(Json::array_iterator a, Json::array_iterator::difference_type i) {
+    return a -= i;
+}
+
+inline Json::const_array_iterator::difference_type operator-(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
+    precondition(a.j_ == b.j_);
+    return a.i_ - b.i_;
+}
+
+class Json::const_iterator { public:
+    typedef std::pair<const String, Json&> value_type;
+    typedef const value_type* pointer_type;
+    typedef const value_type& reference_type;
+    typedef std::forward_iterator_tag iterator_category;
+
+    const_iterator()
+        : value_(String(), *(Json*) 0) {
+    }
+    typedef bool (const_iterator::*unspecified_bool_type)() const;
+    operator unspecified_bool_type() const {
+        return live() ? &const_iterator::live : 0;
+    }
+    bool live() const {
+        return i_ >= 0;
+    }
+    const value_type& operator*() const {
+        return value_;
+    }
+    const value_type* operator->() const {
+        return &(**this);
+    }
+    const String& key() const {
+        return (**this).first;
+    }
+    const Json& value() const {
+        return (**this).second;
+    }
+    void operator++() {
+        ++i_;
+        fix();
+    }
+    void operator++(int) {
+        ++(*this);
+    }
+  private:
+    const Json* j_;
+    int i_;
+    value_type value_;
+    const_iterator(const Json* j, int i)
+        : j_(j), i_(i), value_(String(), *(Json*) 0) {
+        if (i_ >= 0)
+            fix();
+    }
+    void fix() {
+        if (j_->u_.x.type == j_object) {
+            ObjectJson* oj = j_->ojson();
+        retry:
+            if (!oj || i_ >= oj->n_)
+                i_ = -1;
+            else if (oj->item(i_).next_ == -2) {
+                ++i_;
+                goto retry;
+            } else {
+                value_.~pair();
+                new((void *) &value_) value_type(oj->item(i_).v_.first,
+                                                 oj->item(i_).v_.second);
+            }
+        } else {
+            ArrayJson *aj = j_->ajson();
+            if (!aj || unsigned(i_) >= unsigned(aj->size))
+                i_ = -1;
+            else {
+                value_.~pair();
+                new((void *) &value_) value_type(String(i_), aj->a[i_]);
+            }
+        }
+    }
+    friend class Json;
+    friend bool operator==(const const_iterator &, const const_iterator &);
+};
+
+class Json::iterator : public const_iterator { public:
+    typedef value_type* pointer_type;
+    typedef value_type& reference_type;
+
+    iterator() {
+    }
+    value_type& operator*() const {
+        if (j_->u_.x.x->refcount != 1)
+            uniqueify();
+        return const_cast<value_type&>(const_iterator::operator*());
+    }
+    value_type* operator->() const {
+        return &(**this);
+    }
+    Json& value() const {
+        return (**this).second;
+    }
+  private:
+    iterator(Json *j, int i)
+        : const_iterator(j, i) {
+    }
+    void uniqueify() const {
+        if (j_->u_.x.type == j_object)
+            const_cast<Json*>(j_)->hard_uniqueify_object(false);
+        else
+            const_cast<Json*>(j_)->hard_uniqueify_array(false, 0);
+        const_cast<iterator*>(this)->fix();
+    }
+    friend class Json;
+};
+
+inline bool operator==(const Json::const_iterator& a, const Json::const_iterator& b) {
+    return a.j_ == b.j_ && a.i_ == b.i_;
+}
+
+inline bool operator!=(const Json::const_iterator& a, const Json::const_iterator& b) {
+    return !(a == b);
+}
+
+
+template <typename P>
+class Json_proxy_base {
+  public:
+    const Json& cvalue() const {
+        return static_cast<const P *>(this)->cvalue();
+    }
+    Json& value() {
+        return static_cast<P *>(this)->value();
+    }
+    operator const Json&() const {
+        return cvalue();
+    }
+    operator Json&() {
+        return value();
+    }
+    bool truthy() const {
+        return cvalue().truthy();
+    }
+    bool falsy() const {
+        return cvalue().falsy();
+    }
+    operator Json::unspecified_bool_type() const {
+        return cvalue();
+    }
+    bool operator!() const {
+        return !cvalue();
+    }
+    bool is_null() const {
+        return cvalue().is_null();
+    }
+    bool is_int() const {
+        return cvalue().is_int();
+    }
+    bool is_i() const {
+        return cvalue().is_i();
+    }
+    bool is_unsigned() const {
+        return cvalue().is_unsigned();
+    }
+    bool is_u() const {
+        return cvalue().is_u();
+    }
+    bool is_signed() const {
+        return cvalue().is_signed();
+    }
+    bool is_nonnegint() const {
+        return cvalue().is_nonnegint();
+    }
+    bool is_double() const {
+        return cvalue().is_double();
+    }
+    bool is_d() const {
+        return cvalue().is_d();
+    }
+    bool is_number() const {
+        return cvalue().is_number();
+    }
+    bool is_n() const {
+        return cvalue().is_n();
+    }
+    bool is_bool() const {
+        return cvalue().is_bool();
+    }
+    bool is_b() const {
+        return cvalue().is_b();
+    }
+    bool is_string() const {
+        return cvalue().is_string();
+    }
+    bool is_s() const {
+        return cvalue().is_s();
+    }
+    bool is_array() const {
+        return cvalue().is_array();
+    }
+    bool is_a() const {
+        return cvalue().is_a();
+    }
+    bool is_object() const {
+        return cvalue().is_object();
+    }
+    bool is_o() const {
+        return cvalue().is_o();
+    }
+    bool is_primitive() const {
+        return cvalue().is_primitive();
+    }
+    bool empty() const {
+        return cvalue().empty();
+    }
+    Json::size_type size() const {
+        return cvalue().size();
+    }
+    int64_t to_i() const {
+        return cvalue().to_i();
+    }
+    uint64_t to_u() const {
+        return cvalue().to_u();
+    }
+    uint64_t to_u64() const {
+        return cvalue().to_u64();
+    }
+    bool to_i(int& x) const {
+        return cvalue().to_i(x);
+    }
+    bool to_i(unsigned& x) const {
+        return cvalue().to_i(x);
+    }
+    bool to_i(long& x) const {
+        return cvalue().to_i(x);
+    }
+    bool to_i(unsigned long& x) const {
+        return cvalue().to_i(x);
+    }
+    bool to_i(long long& x) const {
+        return cvalue().to_i(x);
+    }
+    bool to_i(unsigned long long& x) const {
+        return cvalue().to_i(x);
+    }
+    int64_t as_i() const {
+        return cvalue().as_i();
+    }
+    int64_t as_i(int64_t default_value) const {
+        return cvalue().as_i(default_value);
+    }
+    uint64_t as_u() const {
+        return cvalue().as_u();
+    }
+    uint64_t as_u(uint64_t default_value) const {
+        return cvalue().as_u(default_value);
+    }
+    double to_d() const {
+        return cvalue().to_d();
+    }
+    bool to_d(double& x) const {
+        return cvalue().to_d(x);
+    }
+    double as_d() const {
+        return cvalue().as_d();
+    }
+    double as_d(double default_value) const {
+        return cvalue().as_d(default_value);
+    }
+    bool to_b() const {
+        return cvalue().to_b();
+    }
+    bool to_b(bool& x) const {
+        return cvalue().to_b(x);
+    }
+    bool as_b() const {
+        return cvalue().as_b();
+    }
+    bool as_b(bool default_value) const {
+        return cvalue().as_b(default_value);
+    }
+    String to_s() const {
+        return cvalue().to_s();
+    }
+    bool to_s(Str& x) const {
+        return cvalue().to_s(x);
+    }
+    bool to_s(String& x) const {
+        return cvalue().to_s(x);
+    }
+    const String& as_s() const {
+        return cvalue().as_s();
+    }
+    const String& as_s(const String& default_value) const {
+        return cvalue().as_s(default_value);
+    }
+    Json::size_type count(Str key) const {
+        return cvalue().count(key);
+    }
+    const Json& get(Str key) const {
+        return cvalue().get(key);
+    }
+    Json& get_insert(const String& key) {
+        return value().get_insert(key);
+    }
+    Json& get_insert(Str key) {
+        return value().get_insert(key);
+    }
+    Json& get_insert(const char* key) {
+        return value().get_insert(key);
+    }
+    long get_i(Str key) const {
+        return cvalue().get_i(key);
+    }
+    double get_d(Str key) const {
+        return cvalue().get_d(key);
+    }
+    bool get_b(Str key) const {
+        return cvalue().get_b(key);
+    }
+    String get_s(Str key) const {
+        return cvalue().get_s(key);
+    }
+    inline const Json_get_proxy get(Str key, Json& x) const;
+    inline const Json_get_proxy get(Str key, int& x) const;
+    inline const Json_get_proxy get(Str key, unsigned& x) const;
+    inline const Json_get_proxy get(Str key, long& x) const;
+    inline const Json_get_proxy get(Str key, unsigned long& x) const;
+    inline const Json_get_proxy get(Str key, long long& x) const;
+    inline const Json_get_proxy get(Str key, unsigned long long& x) const;
+    inline const Json_get_proxy get(Str key, double& x) const;
+    inline const Json_get_proxy get(Str key, bool& x) const;
+    inline const Json_get_proxy get(Str key, Str& x) const;
+    inline const Json_get_proxy get(Str key, String& x) const;
+    const Json& operator[](Str key) const {
+        return cvalue().get(key);
+    }
+    Json_object_proxy<P> operator[](const String& key) {
+        return Json_object_proxy<P>(*static_cast<P*>(this), key);
+    }
+    Json_object_str_proxy<P> operator[](Str key) {
+        return Json_object_str_proxy<P>(*static_cast<P*>(this), key);
+    }
+    Json_object_str_proxy<P> operator[](const char* key) {
+        return Json_object_str_proxy<P>(*static_cast<P*>(this), key);
+    }
+    const Json& at(Str key) const {
+        return cvalue().at(key);
+    }
+    Json& at_insert(const String& key) {
+        return value().at_insert(key);
+    }
+    Json& at_insert(Str key) {
+        return value().at_insert(key);
+    }
+    Json& at_insert(const char* key) {
+        return value().at_insert(key);
+    }
+    inline Json& set(const String& key, Json value) {
+        return this->value().set(key, value);
+    }
+    template <typename Q>
+    inline Json& set(const String& key, const Json_proxy_base<Q>& value) {
+        return this->value().set(key, value);
+    }
+    Json& unset(Str key) {
+        return value().unset(key);
+    }
+    template <typename... Args>
+    inline Json& set_list(Args&&... args) {
+        return value().set_list(std::forward<Args>(args)...);
+    }
+    std::pair<Json::object_iterator, bool> insert(const Json::object_value_type &x) {
+        return value().insert(x);
+    }
+    Json::object_iterator insert(Json::object_iterator position, const Json::object_value_type &x) {
+        return value().insert(position, x);
+    }
+    Json::object_iterator erase(Json::object_iterator it) {
+        return value().erase(it);
+    }
+    Json::size_type erase(Str key) {
+        return value().erase(key);
+    }
+    Json& merge(const Json& x) {
+        return value().merge(x);
+    }
+    template <typename P2> Json& merge(const Json_proxy_base<P2>& x) {
+        return value().merge(x.cvalue());
+    }
+    const Json& get(Json::size_type x) const {
+        return cvalue().get(x);
+    }
+    Json& get_insert(Json::size_type x) {
+        return value().get_insert(x);
+    }
+    const Json& operator[](int key) const {
+        return cvalue().at(key);
+    }
+    Json_array_proxy<P> operator[](int key) {
+        return Json_array_proxy<P>(*static_cast<P*>(this), key);
+    }
+    const Json& at(Json::size_type x) const {
+        return cvalue().at(x);
+    }
+    Json& at_insert(Json::size_type x) {
+        return value().at_insert(x);
+    }
+    const Json& back() const {
+        return cvalue().back();
+    }
+    Json& back() {
+        return value().back();
+    }
+    Json& push_back(Json x) {
+        return value().push_back(std::move(x));
+    }
+    template <typename Q> Json& push_back(const Json_proxy_base<Q>& x) {
+        return value().push_back(x);
+    }
+    void pop_back() {
+        value().pop_back();
+    }
+    template <typename... Args> Json& push_back_list(Args&&... args) {
+        return value().push_back_list(std::forward<Args>(args)...);
+    }
+    Json::array_iterator insert(Json::array_iterator position, Json x) {
+        return value().insert(position, std::move(x));
+    }
+    template <typename Q> Json::array_iterator insert(Json::array_iterator position, const Json_proxy_base<Q>& x) {
+        return value().insert(position, x);
+    }
+    Json::array_iterator erase(Json::array_iterator first, Json::array_iterator last) {
+        return value().erase(first, last);
+    }
+    Json::array_iterator erase(Json::array_iterator position) {
+        return value().erase(position);
+    }
+    void resize(Json::size_type n) {
+        value().resize(n);
+    }
+    void unparse(StringAccum& sa) const {
+        return cvalue().unparse(sa);
+    }
+    void unparse(StringAccum& sa, const Json::unparse_manipulator& m) const {
+        return cvalue().unparse(sa, m);
+    }
+    String unparse() const {
+        return cvalue().unparse();
+    }
+    String unparse(const Json::unparse_manipulator& m) const {
+        return cvalue().unparse(m);
+    }
+    bool assign_parse(const String& str) {
+        return value().assign_parse(str);
+    }
+    bool assign_parse(const char* first, const char* last) {
+        return value().assign_parse(first, last);
+    }
+    void swap(Json& x) {
+        value().swap(x);
+    }
+    Json& operator++() {
+        return ++value();
+    }
+    void operator++(int) {
+        value()++;
+    }
+    Json& operator--() {
+        return --value();
+    }
+    void operator--(int) {
+        value()--;
+    }
+    Json& operator+=(int x) {
+        return value() += x;
+    }
+    Json& operator+=(unsigned x) {
+        return value() += x;
+    }
+    Json& operator+=(long x) {
+        return value() += x;
+    }
+    Json& operator+=(unsigned long x) {
+        return value() += x;
+    }
+    Json& operator+=(long long x) {
+        return value() += x;
+    }
+    Json& operator+=(unsigned long long x) {
+        return value() += x;
+    }
+    Json& operator+=(double x) {
+        return value() += x;
+    }
+    Json& operator+=(const Json& x) {
+        return value() += x;
+    }
+    Json& operator-=(int x) {
+        return value() -= x;
+    }
+    Json& operator-=(unsigned x) {
+        return value() -= x;
+    }
+    Json& operator-=(long x) {
+        return value() -= x;
+    }
+    Json& operator-=(unsigned long x) {
+        return value() -= x;
+    }
+    Json& operator-=(long long x) {
+        return value() -= x;
+    }
+    Json& operator-=(unsigned long long x) {
+        return value() -= x;
+    }
+    Json& operator-=(double x) {
+        return value() -= x;
+    }
+    Json& operator-=(const Json& x) {
+        return value() -= x;
+    }
+    Json::const_object_iterator obegin() const {
+        return cvalue().obegin();
+    }
+    Json::const_object_iterator oend() const {
+        return cvalue().oend();
+    }
+    Json::object_iterator obegin() {
+        return value().obegin();
+    }
+    Json::object_iterator oend() {
+        return value().oend();
+    }
+    Json::const_object_iterator cobegin() const {
+        return cvalue().cobegin();
+    }
+    Json::const_object_iterator coend() const {
+        return cvalue().coend();
+    }
+    Json::const_array_iterator abegin() const {
+        return cvalue().abegin();
+    }
+    Json::const_array_iterator aend() const {
+        return cvalue().aend();
+    }
+    Json::array_iterator abegin() {
+        return value().abegin();
+    }
+    Json::array_iterator aend() {
+        return value().aend();
+    }
+    Json::const_array_iterator cabegin() const {
+        return cvalue().cabegin();
+    }
+    Json::const_array_iterator caend() const {
+        return cvalue().caend();
+    }
+    Json::const_iterator begin() const {
+        return cvalue().begin();
+    }
+    Json::const_iterator end() const {
+        return cvalue().end();
+    }
+    Json::iterator begin() {
+        return value().begin();
+    }
+    Json::iterator end() {
+        return value().end();
+    }
+    Json::const_iterator cbegin() const {
+        return cvalue().cbegin();
+    }
+    Json::const_iterator cend() const {
+        return cvalue().cend();
+    }
+};
+
+template <typename T>
+class Json_object_proxy : public Json_proxy_base<Json_object_proxy<T> > {
+  public:
+    const Json& cvalue() const {
+        return base_.get(key_);
+    }
+    Json& value() {
+        return base_.get_insert(key_);
+    }
+    Json& operator=(const Json& x) {
+        return value() = x;
+    }
+#if HAVE_CXX_RVALUE_REFERENCES
+    Json& operator=(Json&& x) {
+        return value() = std::move(x);
+    }
+#endif
+    Json& operator=(const Json_object_proxy<T>& x) {
+        return value() = x.cvalue();
+    }
+    template <typename P> Json& operator=(const Json_proxy_base<P>& x) {
+        return value() = x.cvalue();
+    }
+    Json_object_proxy(T& ref, const String& key)
+        : base_(ref), key_(key) {
+    }
+    T &base_;
+    String key_;
+};
+
+template <typename T>
+class Json_object_str_proxy : public Json_proxy_base<Json_object_str_proxy<T> > {
+  public:
+    const Json& cvalue() const {
+        return base_.get(key_);
+    }
+    Json& value() {
+        return base_.get_insert(key_);
+    }
+    Json& operator=(const Json& x) {
+        return value() = x;
+    }
+#if HAVE_CXX_RVALUE_REFERENCES
+    Json& operator=(Json&& x) {
+        return value() = std::move(x);
+    }
+#endif
+    Json& operator=(const Json_object_str_proxy<T>& x) {
+        return value() = x.cvalue();
+    }
+    template <typename P> Json& operator=(const Json_proxy_base<P>& x) {
+        return value() = x.cvalue();
+    }
+    Json_object_str_proxy(T& ref, Str key)
+        : base_(ref), key_(key) {
+    }
+    T &base_;
+    Str key_;
+};
+
+template <typename T>
+class Json_array_proxy : public Json_proxy_base<Json_array_proxy<T> > {
+  public:
+    const Json& cvalue() const {
+        return base_.get(key_);
+    }
+    Json& value() {
+        return base_.get_insert(key_);
+    }
+    Json& operator=(const Json& x) {
+        return value() = x;
+    }
+#if HAVE_CXX_RVALUE_REFERENCES
+    Json& operator=(Json&& x) {
+        return value() = std::move(x);
+    }
+#endif
+    Json& operator=(const Json_array_proxy<T>& x) {
+        return value() = x.cvalue();
+    }
+    template <typename P> Json& operator=(const Json_proxy_base<P>& x) {
+        return value() = x.cvalue();
+    }
+    Json_array_proxy(T& ref, int key)
+        : base_(ref), key_(key) {
+    }
+    T &base_;
+    int key_;
+};
+
+class Json_get_proxy : public Json_proxy_base<Json_get_proxy> {
+  public:
+    const Json& cvalue() const {
+        return base_;
+    }
+    operator Json::unspecified_bool_type() const {
+        return status_ ? &Json::is_null : 0;
+    }
+    bool operator!() const {
+        return !status_;
+    }
+    bool status() const {
+        return status_;
+    }
+    const Json_get_proxy& status(bool& x) const {
+        x = status_;
+        return *this;
+    }
+    Json_get_proxy(const Json& ref, bool status)
+        : base_(ref), status_(status) {
+    }
+    const Json& base_;
+    bool status_;
+  private:
+    Json_get_proxy& operator=(const Json_get_proxy& x);
+};
+
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, Json& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, int& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, unsigned& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, long& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, unsigned long& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, long long& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, unsigned long long& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, double& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, bool& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, Str& x) const {
+    return cvalue().get(key, x);
+}
+template <typename T>
+inline const Json_get_proxy Json_proxy_base<T>::get(Str key, String& x) const {
+    return cvalue().get(key, x);
+}
+
+
+/** @brief Construct a null Json. */
+inline Json::Json() {
+    memset(&u_, 0, sizeof(u_));
+}
+/** @brief Construct a null Json. */
+inline Json::Json(const null_t&) {
+    memset(&u_, 0, sizeof(u_));
+}
+/** @brief Construct a Json copy of @a x. */
+inline Json::Json(const Json& x)
+    : u_(x.u_) {
+    if (u_.x.type < 0)
+        u_.str.ref();
+    if (u_.x.x && (u_.x.type == j_array || u_.x.type == j_object))
+        u_.x.x->ref();
+}
+/** @overload */
+template <typename P> inline Json::Json(const Json_proxy_base<P>& x)
+    : u_(x.cvalue().u_) {
+    if (u_.x.type < 0)
+        u_.str.ref();
+    if (u_.x.x && (u_.x.type == j_array || u_.x.type == j_object))
+        u_.x.x->ref();
+}
+#if HAVE_CXX_RVALUE_REFERENCES
+/** @overload */
+inline Json::Json(Json&& x)
+    : u_(std::move(x.u_)) {
+    memset(&x, 0, sizeof(x));
+}
+#endif
+/** @brief Construct simple Json values. */
+inline Json::Json(int x) {
+    u_.i.x = x;
+    u_.i.type = j_int;
+}
+inline Json::Json(unsigned x) {
+    u_.u.x = x;
+    u_.u.type = j_unsigned;
+}
+inline Json::Json(long x) {
+    u_.i.x = x;
+    u_.i.type = j_int;
+}
+inline Json::Json(unsigned long x) {
+    u_.u.x = x;
+    u_.u.type = j_unsigned;
+}
+inline Json::Json(long long x) {
+    u_.i.x = x;
+    u_.i.type = j_int;
+}
+inline Json::Json(unsigned long long x) {
+    u_.u.x = x;
+    u_.u.type = j_unsigned;
+}
+inline Json::Json(double x) {
+    u_.d.x = x;
+    u_.d.type = j_double;
+}
+inline Json::Json(bool x) {
+    u_.i.x = x;
+    u_.i.type = j_bool;
+}
+inline Json::Json(const String& x) {
+    u_.str = x.internal_rep();
+    u_.str.ref();
+}
+inline Json::Json(Str x) {
+    u_.str.reset_ref();
+    String str(x);
+    str.swap(u_.str);
+}
+inline Json::Json(const char* x) {
+    u_.str.reset_ref();
+    String str(x);
+    str.swap(u_.str);
+}
+/** @brief Construct an array Json containing the elements of @a x. */
+template <typename T>
+inline Json::Json(const std::vector<T> &x) {
+    u_.a.type = j_array;
+    u_.a.x = ArrayJson::make(int(x.size()));
+    for (typename std::vector<T>::const_iterator it = x.begin();
+         it != x.end(); ++it) {
+        new((void*) &u_.a.x->a[u_.a.x->size]) Json(*it);
+        ++u_.a.x->size;
+    }
+}
+
+template <typename T> struct Json_iterator_initializer {
+    template <typename I>
+    static inline void run(Json& j, I first, I last) {
+        for (j = Json::make_array(); first != last; ++first)
+            j.push_back(Json(*first));
+    }
+};
+template <typename T, typename U>
+struct Json_iterator_initializer<std::pair<T, U> > {
+    template <typename I>
+    static inline void run(Json& j, I first, I last) {
+        for (j = Json::make_object(); first != last; ++first)
+            j.set((*first).first, (*first).second);
+    }
+};
+
+/** @brief Construct an array Json containing the elements in [@a first,
+    @a last). */
+template <typename T>
+inline Json::Json(T first, T last) {
+    u_.a.type = 0;
+    Json_iterator_initializer<typename std::iterator_traits<T>::value_type>::run(*this, first, last);
+}
+
+/** @cond never */
+inline void Json::deref() {
+    if (u_.x.type < 0)
+        u_.str.deref();
+    else if (u_.x.x && unsigned(u_.x.type - j_array) < 2)
+        u_.x.x->deref(json_type(u_.x.type));
+}
+/** @endcond never */
+
+inline Json::~Json() {
+    deref();
+}
+
+/** @brief Return an empty array-valued Json. */
+inline Json Json::make_array() {
+    Json j;
+    j.u_.x.type = j_array;
+    return j;
+}
+/** @brief Return an array-valued Json containing @a args. */
+template <typename... Args>
+inline Json Json::array(Args&&... args) {
+    Json j;
+    j.u_.x.type = j_array;
+    j.reserve(sizeof...(args));
+    j.push_back_list(std::forward<Args>(args)...);
+    return j;
+}
+/** @brief Return an empty array-valued Json with reserved space for @a n items. */
+inline Json Json::make_array_reserve(int n) {
+    Json j;
+    j.u_.a.type = j_array;
+    j.u_.a.x = n ? ArrayJson::make(n) : 0;
+    return j;
+}
+/** @brief Return an empty object-valued Json. */
+inline Json Json::make_object() {
+    Json j;
+    j.u_.o.type = j_object;
+    return j;
+}
+/** @brief Return an empty object-valued Json. */
+template <typename... Args>
+inline Json Json::object(Args&&... rest) {
+    Json j;
+    j.u_.o.type = j_object;
+    j.set_list(std::forward<Args>(rest)...);
+    return j;
+}
+/** @brief Return a string-valued Json. */
+inline Json Json::make_string(const String &x) {
+    return Json(x);
+}
+/** @overload */
+inline Json Json::make_string(const char *s, int len) {
+    return Json(String(s, len));
+}
+
+/** @brief Test if this Json is truthy. */
+inline bool Json::truthy() const {
+    return (u_.x.x ? u_.x.type >= 0 || u_.str.length
+            : (unsigned) (u_.x.type - 1) < (unsigned) (j_int - 1));
+}
+/** @brief Test if this Json is falsy. */
+inline bool Json::falsy() const {
+    return !truthy();
+}
+/** @brief Test if this Json is truthy.
+    @sa empty() */
+inline Json::operator unspecified_bool_type() const {
+    return truthy() ? &Json::is_null : 0;
+}
+/** @brief Return true if this Json is falsy. */
+inline bool Json::operator!() const {
+    return !truthy();
+}
+
+/** @brief Test this Json's type. */
+inline bool Json::is_null() const {
+    return !u_.x.x && u_.x.type == j_null;
+}
+inline bool Json::is_int() const {
+    return unsigned(u_.x.type - j_int) <= unsigned(j_unsigned - j_int);
+}
+inline bool Json::is_i() const {
+    return is_int();
+}
+inline bool Json::is_unsigned() const {
+    return u_.x.type == j_unsigned;
+}
+inline bool Json::is_u() const {
+    return is_unsigned();
+}
+inline bool Json::is_signed() const {
+    return u_.x.type == j_int;
+}
+inline bool Json::is_nonnegint() const {
+    return u_.x.type == j_unsigned
+        || (u_.x.type == j_int && u_.i.x >= 0);
+}
+inline bool Json::is_double() const {
+    return u_.x.type == j_double;
+}
+inline bool Json::is_d() const {
+    return is_double();
+}
+inline bool Json::is_number() const {
+    return unsigned(u_.x.type - j_int) <= unsigned(j_double - j_int);
+}
+inline bool Json::is_n() const {
+    return is_number();
+}
+inline bool Json::is_bool() const {
+    return u_.x.type == j_bool;
+}
+inline bool Json::is_b() const {
+    return is_bool();
+}
+inline bool Json::is_string() const {
+    return u_.x.x && u_.x.type <= 0;
+}
+inline bool Json::is_s() const {
+    return is_string();
+}
+inline bool Json::is_array() const {
+    return u_.x.type == j_array;
+}
+inline bool Json::is_a() const {
+    return is_array();
+}
+inline bool Json::is_object() const {
+    return u_.x.type == j_object;
+}
+inline bool Json::is_o() const {
+    return is_object();
+}
+/** @brief Test if this Json is a primitive value, not including null. */
+inline bool Json::is_primitive() const {
+    return u_.x.type >= j_int || (u_.x.x && u_.x.type <= 0);
+}
+
+/** @brief Return true if this Json is null, an empty array, or an empty
+    object. */
+inline bool Json::empty() const {
+    return unsigned(u_.x.type) < unsigned(j_int)
+        && (!u_.x.x || u_.x.x->size == 0);
+}
+/** @brief Return the number of elements in this complex Json.
+    @pre is_array() || is_object() || is_null() */
+inline Json::size_type Json::size() const {
+    precondition(unsigned(u_.x.type) < unsigned(j_int));
+    return u_.x.x ? u_.x.x->size : 0;
+}
+/** @brief Test if this complex Json is shared. */
+inline bool Json::shared() const {
+    return u_.x.x && (u_.x.type == j_array || u_.x.type == j_object)
+        && u_.x.x->refcount != 1;
+}
+
+// Primitive methods
+
+/** @brief Return this Json converted to an integer.
+
+    Converts any Json to an integer value. Numeric Jsons convert as you'd
+    expect. Null Jsons convert to 0; false boolean Jsons to 0 and true
+    boolean Jsons to 1; string Jsons to a number parsed from their initial
+    portions; and array and object Jsons to size().
+    @sa as_i() */
+inline int64_t Json::to_i() const {
+    if (is_int())
+        return u_.i.x;
+    else
+        return hard_to_i();
+}
+
+inline uint64_t Json::to_u() const {
+    if (is_int())
+        return u_.u.x;
+    else
+        return hard_to_u();
+}
+
+/** @brief Extract this integer Json's value into @a x.
+    @param[out] x value storage
+    @return True iff this Json stores an integer value.
+
+    If false is returned (!is_number() or the number is not parseable as a
+    pure integer), @a x remains unchanged. */
+inline bool Json::to_i(int& x) const {
+    if (is_int()) {
+        x = u_.i.x;
+        return true;
+    } else if (is_double() && u_.d.x == double(int(u_.d.x))) {
+        x = int(u_.d.x);
+        return true;
+    } else
+        return false;
+}
+
+/** @overload */
+inline bool Json::to_i(unsigned& x) const {
+    if (is_int()) {
+        x = u_.u.x;
+        return true;
+    } else if (is_double() && u_.d.x == double(unsigned(u_.d.x))) {
+        x = unsigned(u_.d.x);
+        return true;
+    } else
+        return false;
+}
+
+/** @overload */
+inline bool Json::to_i(long& x) const {
+    if (is_int()) {
+        x = u_.i.x;
+        return true;
+    } else if (is_double() && u_.d.x == double(long(u_.d.x))) {
+        x = long(u_.d.x);
+        return true;
+    } else
+        return false;
+}
+
+/** @overload */
+inline bool Json::to_i(unsigned long& x) const {
+    if (is_int()) {
+        x = u_.u.x;
+        return true;
+    } else if (is_double() && u_.d.x == double((unsigned long) u_.d.x)) {
+        x = (unsigned long) u_.d.x;
+        return true;
+    } else
+        return false;
+}
+
+/** @overload */
+inline bool Json::to_i(long long& x) const {
+    if (is_int()) {
+        x = u_.i.x;
+        return true;
+    } else if (is_double() && u_.d.x == double((long long) u_.d.x)) {
+        x = (long long) u_.d.x;
+        return true;
+    } else
+        return false;
+}
+
+/** @overload */
+inline bool Json::to_i(unsigned long long& x) const {
+    if (is_int()) {
+        x = u_.u.x;
+        return true;
+    } else if (is_double() && u_.d.x == double((unsigned long long) u_.d.x)) {
+        x = (unsigned long long) u_.d.x;
+        return true;
+    } else
+        return false;
+}
+
+/** @brief Return this Json converted to a 64-bit unsigned integer.
+
+    See to_i() for the conversion rules. */
+inline uint64_t Json::to_u64() const {
+    return to_u();
+}
+
+/** @brief Return the integer value of this numeric Json.
+    @pre is_number()
+    @sa to_i() */
+inline int64_t Json::as_i() const {
+    precondition(is_int() || is_double());
+    return is_int() ? u_.i.x : int64_t(u_.d.x);
+}
+
+/** @brief Return the integer value of this numeric Json or @a default_value. */
+inline int64_t Json::as_i(int64_t default_value) const {
+    if (is_int() || is_double())
+        return as_i();
+    else
+        return default_value;
+}
+
+/** @brief Return the unsigned integer value of this numeric Json.
+    @pre is_number()
+    @sa to_i() */
+inline uint64_t Json::as_u() const {
+    precondition(is_int() || is_double());
+    return is_int() ? u_.u.x : uint64_t(u_.d.x);
+}
+
+/** @brief Return the integer value of this numeric Json or @a default_value. */
+inline uint64_t Json::as_u(uint64_t default_value) const {
+    if (is_int() || is_double())
+        return as_u();
+    else
+        return default_value;
+}
+
+/** @brief Return this Json converted to a double.
+
+    Converts any Json to an integer value. Numeric Jsons convert as you'd
+    expect. Null Jsons convert to 0; false boolean Jsons to 0 and true
+    boolean Jsons to 1; string Jsons to a number parsed from their initial
+    portions; and array and object Jsons to size().
+    @sa as_d() */
+inline double Json::to_d() const {
+    if (is_double())
+        return u_.d.x;
+    else
+        return hard_to_d();
+}
+
+/** @brief Extract this numeric Json's value into @a x.
+    @param[out] x value storage
+    @return True iff is_number().
+
+    If !is_number(), @a x remains unchanged. */
+inline bool Json::to_d(double& x) const {
+    if (is_double() || is_int()) {
+        x = to_d();
+        return true;
+    } else
+        return false;
+}
+
+/** @brief Return the double value of this numeric Json.
+    @pre is_number()
+    @sa to_d() */
+inline double Json::as_d() const {
+    precondition(is_double() || is_int());
+    if (is_double())
+        return u_.d.x;
+    else if (u_.x.type == j_int)
+        return u_.i.x;
+    else
+        return u_.u.x;
+}
+
+/** @brief Return the double value of this numeric Json or @a default_value. */
+inline double Json::as_d(double default_value) const {
+    if (!is_double() && !is_int())
+        return default_value;
+    else
+        return as_d();
+}
+
+/** @brief Return this Json converted to a boolean.
+
+    Converts any Json to a boolean value. Boolean Jsons convert as you'd
+    expect. Null Jsons convert to false; zero-valued numeric Jsons to false,
+    and other numeric Jsons to true; empty string Jsons to false, and other
+    string Jsons to true; and array and object Jsons to !empty().
+    @sa as_b() */
+inline bool Json::to_b() const {
+    if (is_bool())
+        return u_.i.x;
+    else
+        return hard_to_b();
+}
+
+/** @brief Extract this boolean Json's value into @a x.
+    @param[out] x value storage
+    @return True iff is_bool().
+
+    If !is_bool(), @a x remains unchanged. */
+inline bool Json::to_b(bool& x) const {
+    if (is_bool()) {
+        x = u_.i.x;
+        return true;
+    } else
+        return false;
+}
+
+/** @brief Return the value of this boolean Json.
+    @pre is_bool()
+    @sa to_b() */
+inline bool Json::as_b() const {
+    precondition(is_bool());
+    return u_.i.x;
+}
+
+/** @brief Return the boolean value of this numeric Json or @a default_value. */
+inline bool Json::as_b(bool default_value) const {
+    if (is_bool())
+        return as_b();
+    else
+        return default_value;
+}
+
+/** @brief Return this Json converted to a string.
+
+    Converts any Json to a string value. String Jsons convert as you'd expect.
+    Null Jsons convert to the empty string; numeric Jsons to their string
+    values; boolean Jsons to "false" or "true"; and array and object Jsons to
+    "[Array]" and "[Object]", respectively.
+    @sa as_s() */
+inline String Json::to_s() const {
+    if (u_.x.type <= 0 && u_.x.x)
+        return String(u_.str);
+    else
+        return hard_to_s();
+}
+
+/** @brief Extract this string Json's value into @a x.
+    @param[out] x value storage
+    @return True iff is_string().
+
+    If !is_string(), @a x remains unchanged. */
+inline bool Json::to_s(Str& x) const {
+    if (u_.x.type <= 0 && u_.x.x) {
+        x.assign(u_.str.data, u_.str.length);
+        return true;
+    } else
+        return false;
+}
+
+/** @brief Extract this string Json's value into @a x.
+    @param[out] x value storage
+    @return True iff is_string().
+
+    If !is_string(), @a x remains unchanged. */
+inline bool Json::to_s(String& x) const {
+    if (u_.x.type <= 0 && u_.x.x) {
+        x.assign(u_.str);
+        return true;
+    } else
+        return false;
+}
+
+/** @brief Return the value of this string Json.
+    @pre is_string()
+    @sa to_s() */
+inline const String& Json::as_s() const {
+    precondition(u_.x.type <= 0 && u_.x.x);
+    return reinterpret_cast<const String&>(u_.str);
+}
+
+/** @overload */
+inline String& Json::as_s() {
+    precondition(u_.x.type <= 0 && u_.x.x);
+    return reinterpret_cast<String&>(u_.str);
+}
+
+/** @brief Return the value of this string Json or @a default_value. */
+inline const String& Json::as_s(const String& default_value) const {
+    if (u_.x.type > 0 || !u_.x.x)
+        return default_value;
+    else
+        return as_s();
+}
+
+inline void Json::force_number() {
+    precondition((u_.x.type == j_null && !u_.x.x) || u_.x.type == j_int || u_.x.type == j_double);
+    if (u_.x.type == j_null && !u_.x.x)
+        u_.x.type = j_int;
+}
+
+inline void Json::force_double() {
+    precondition((u_.x.type == j_null && !u_.x.x) || u_.x.type == j_int || u_.x.type == j_double);
+    if (u_.x.type == j_null && !u_.x.x)
+        u_.x.type = j_double;
+    else if (u_.x.type == j_int) {
+        u_.d.x = u_.i.x;
+        u_.d.type = j_double;
+    } else if (u_.x.type == j_unsigned) {
+        u_.d.x = u_.u.x;
+        u_.d.type = j_double;
+    }
+}
+
+
+// Object methods
+
+/** @brief Return 1 if this object Json contains @a key, 0 otherwise.
+
+    Returns 0 if this is not an object Json. */
+inline Json::size_type Json::count(Str key) const {
+    precondition(u_.x.type == j_null || u_.x.type == j_object);
+    return u_.o.x ? ojson()->find(key.data(), key.length()) >= 0 : 0;
+}
+
+/** @brief Return the value at @a key in an object or array Json.
+
+    If this is an array Json, and @a key is the simplest base-10
+    representation of an integer <em>i</em>, then returns get(<em>i</em>). If
+    this is neither an array nor an object, returns a null Json. */
+inline const Json& Json::get(Str key) const {
+    int i;
+    ObjectJson *oj;
+    if (is_object() && (oj = ojson())
+        && (i = oj->find(key.data(), key.length())) >= 0)
+        return oj->item(i).v_.second;
+    else
+        return hard_get(key);
+}
+
+/** @brief Return a reference to the value of @a key in an object Json.
+
+    This Json is first converted to an object. Arrays are converted to objects
+    with numeric keys. Other types of Json are converted to empty objects.
+    If !count(@a key), then a null Json is inserted at @a key. */
+inline Json& Json::get_insert(const String &key) {
+    uniqueify_object(true);
+    return ojson()->get_insert(key);
+}
+
+/** @overload */
+inline Json& Json::get_insert(Str key) {
+    uniqueify_object(true);
+    return ojson()->get_insert(key);
+}
+
+/** @overload */
+inline Json& Json::get_insert(const char *key) {
+    uniqueify_object(true);
+    return ojson()->get_insert(Str(key));
+}
+
+/** @brief Return get(@a key).to_i(). */
+inline long Json::get_i(Str key) const {
+    return get(key).to_i();
+}
+
+/** @brief Return get(@a key).to_d(). */
+inline double Json::get_d(Str key) const {
+    return get(key).to_d();
+}
+
+/** @brief Return get(@a key).to_b(). */
+inline bool Json::get_b(Str key) const {
+    return get(key).to_b();
+}
+
+/** @brief Return get(@a key).to_s(). */
+inline String Json::get_s(Str key) const {
+    return get(key).to_s();
+}
+
+/** @brief Extract this object Json's value at @a key into @a x.
+    @param[out] x value storage
+    @return proxy for *this
+
+    @a x is assigned iff count(@a key). The return value is a proxy
+    object that mostly behaves like *this. However, the proxy is "truthy"
+    iff count(@a key) and @a x was assigned. The proxy also has status()
+    methods that return the extraction status. For example:
+
+    <code>
+    Json j = Json::parse("{\"a\":1,\"b\":2}"), x;
+    assert(j.get("a", x));            // extraction succeeded
+    assert(x == Json(1));
+    assert(!j.get("c", x));           // no "c" key
+    assert(x == Json(1));             // x remains unchanged
+    assert(!j.get("c", x).status());  // can use ".status()" for clarity
+
+    // Can chain .get() methods to extract multiple values
+    Json a, b, c;
+    j.get("a", a).get("b", b);
+    assert(a == Json(1) && b == Json(2));
+
+    // Use .status() to return or assign extraction status
+    bool a_status, b_status, c_status;
+    j.get("a", a).status(a_status)
+     .get("b", b).status(b_status)
+     .get("c", c).status(c_status);
+    assert(a_status && b_status && !c_status);
+    </code>
+
+    Overloaded versions of @a get() can extract integer, double, boolean,
+    and string values for specific keys. These versions succeed iff
+    count(@a key) and the corresponding value has the expected type. For
+    example:
+
+    <code>
+    Json j = Json::parse("{\"a\":1,\"b\":\"\"}");
+    int a, b;
+    bool a_status, b_status;
+    j.get("a", a).status(a_status).get("b", b).status(b_status);
+    assert(a_status && a == 1 && !b_status);
+    </code> */
+inline const Json_get_proxy Json::get(Str key, Json& x) const {
+    int i;
+    ObjectJson *oj;
+    if (is_object() && (oj = ojson())
+        && (i = oj->find(key.data(), key.length())) >= 0) {
+        x = oj->item(i).v_.second;
+        return Json_get_proxy(*this, true);
+    } else
+        return Json_get_proxy(*this, false);
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, int &x) const {
+    return Json_get_proxy(*this, get(key).to_i(x));
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, unsigned& x) const {
+    return Json_get_proxy(*this, get(key).to_i(x));
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, long& x) const {
+    return Json_get_proxy(*this, get(key).to_i(x));
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, unsigned long& x) const {
+    return Json_get_proxy(*this, get(key).to_i(x));
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, long long& x) const {
+    return Json_get_proxy(*this, get(key).to_i(x));
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, unsigned long long& x) const {
+    return Json_get_proxy(*this, get(key).to_i(x));
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, double& x) const {
+    return Json_get_proxy(*this, get(key).to_d(x));
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, bool& x) const {
+    return Json_get_proxy(*this, get(key).to_b(x));
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, Str& x) const {
+    return Json_get_proxy(*this, get(key).to_s(x));
+}
+
+/** @overload */
+inline const Json_get_proxy Json::get(Str key, String& x) const {
+    return Json_get_proxy(*this, get(key).to_s(x));
+}
+
+
+/** @brief Return the value at @a key in an object or array Json.
+    @sa Json::get() */
+inline const Json& Json::operator[](Str key) const {
+    return get(key);
+}
+
+/** @brief Return a proxy reference to the value at @a key in an object Json.
+
+    Returns the current @a key value if it exists. Otherwise, returns a proxy
+    that acts like a null Json. If this proxy is assigned, this Json is
+    converted to an object as by get_insert(), and then extended as necessary
+    to contain the new value. */
+inline Json_object_proxy<Json> Json::operator[](const String& key) {
+    return Json_object_proxy<Json>(*this, key);
+}
+
+/** @overload */
+inline Json_object_str_proxy<Json> Json::operator[](Str key) {
+    return Json_object_str_proxy<Json>(*this, key);
+}
+
+/** @overload */
+inline Json_object_str_proxy<Json> Json::operator[](const char* key) {
+    return Json_object_str_proxy<Json>(*this, key);
+}
+
+/** @brief Return the value at @a key in an object Json.
+    @pre is_object() && count(@a key) */
+inline const Json& Json::at(Str key) const {
+    precondition(is_object() && u_.o.x);
+    ObjectJson *oj = ojson();
+    int i = oj->find(key.data(), key.length());
+    precondition(i >= 0);
+    return oj->item(i).v_.second;
+}
+
+/** @brief Return a reference to the value at @a key in an object Json.
+    @pre is_object()
+
+    Returns a newly-inserted null Json if !count(@a key). */
+inline Json& Json::at_insert(const String &key) {
+    precondition(is_object());
+    return get_insert(key);
+}
+
+/** @overload */
+inline Json& Json::at_insert(Str key) {
+    precondition(is_object());
+    return get_insert(key);
+}
+
+/** @overload */
+inline Json& Json::at_insert(const char *key) {
+    precondition(is_object());
+    return get_insert(Str(key));
+}
+
+/** @brief Set the value of @a key to @a value in this object Json.
+    @return this Json
+
+    An array Json is converted to an object Json with numeric keys. Other
+    non-object Jsons are converted to empty objects. */
+inline Json& Json::set(const String& key, Json value) {
+    uniqueify_object(true);
+    ojson()->get_insert(key) = std::move(value);
+    return *this;
+}
+
+/** @overload */
+template <typename P>
+inline Json& Json::set(const String& key, const Json_proxy_base<P>& value) {
+    uniqueify_object(true);
+    ojson()->get_insert(key) = value.cvalue();
+    return *this;
+}
+
+/** @brief Remove the value of @a key from an object Json.
+    @return this Json
+    @sa erase() */
+inline Json& Json::unset(Str key) {
+    if (is_object()) {
+        uniqueify_object(true);
+        ojson()->erase(key);
+    }
+    return *this;
+}
+
+/** @brief Add the key-value pairs [key, value, ...] to the object.
+    @return this Json
+
+    An array Json is converted to an object Json with numeric keys. Other
+    non-object Jsons are converted to empty objects. */
+template <typename T, typename... Args>
+inline Json& Json::set_list(const String& key, T value, Args&&... rest) {
+    set(key, std::move(value));
+    set_list(std::forward<Args>(rest)...);
+    return *this;
+}
+
+/** @overload */
+inline Json& Json::set_list() {
+    return *this;
+}
+
+/** @brief Insert element @a x in this object Json.
+    @param x Pair of key and value.
+    @return Pair of iterator pointing to key's value and bool indicating
+    whether the value is newly inserted.
+    @pre is_object()
+
+    An existing element with key @a x.first is not replaced. */
+inline std::pair<Json::object_iterator, bool> Json::insert(const object_value_type& x) {
+    precondition(is_object());
+    uniqueify_object(false);
+    ObjectJson *oj = ojson();
+    int n = oj->n_, i = oj->find_insert(x.first, x.second);
+    return std::make_pair(object_iterator(this, i), i == n);
+}
+
+/** @brief Insert element @a x in this object Json.
+    @param position Ignored.
+    @param x Pair of key and value.
+    @return Pair of iterator pointing to key's value and bool indicating
+    whether the value is newly inserted.
+    @pre is_object()
+
+    An existing element with key @a x.first is not replaced. */
+inline Json::object_iterator Json::insert(object_iterator position, const object_value_type& x) {
+    (void) position;
+    return insert(x).first;
+}
+
+/** @brief Remove the value pointed to by @a it from an object Json.
+    @pre is_object()
+    @return Next iterator */
+inline Json::object_iterator Json::erase(Json::object_iterator it) {
+    precondition(is_object() && it.live() && it.j_ == this);
+    uniqueify_object(false);
+    ojson()->erase(it.i_);
+    ++it;
+    return it;
+}
+
+/** @brief Remove the value of @a key from an object Json.
+    @pre is_object()
+    @return Number of items removed */
+inline Json::size_type Json::erase(Str key) {
+    precondition(is_object());
+    uniqueify_object(false);
+    return ojson()->erase(key);
+}
+
+/** @brief Merge the values of object Json @a x into this object Json.
+    @pre (is_object() || is_null()) && (x.is_object() || x.is_null())
+    @return this Json
+
+    The key-value pairs in @a x are assigned to this Json. Null Jsons are
+    silently converted to empty objects, except that if @a x and this Json are
+    both null, then this Json is left as null. */
+inline Json& Json::merge(const Json& x) {
+    precondition(is_object() || is_null());
+    precondition(x.is_object() || x.is_null());
+    if (x.u_.o.x) {
+        uniqueify_object(false);
+        ObjectJson *oj = ojson(), *xoj = x.ojson();
+        const ObjectItem *xb = xoj->os_, *xe = xb + xoj->n_;
+        for (; xb != xe; ++xb)
+            if (xb->next_ > -2)
+                oj->get_insert(xb->v_.first) = xb->v_.second;
+    }
+    return *this;
+}
+
+/** @cond never */
+template <typename U>
+inline Json& Json::merge(const Json_proxy_base<U>& x) {
+    return merge(x.cvalue());
+}
+/** @endcond never */
+
+
+// ARRAY METHODS
+
+/** @brief Return the @a x th array element.
+
+    If @a x is out of range of this array, returns a null Json. If this is an
+    object Json, then returns get(String(@a x)). If this is neither an object
+    nor an array, returns a null Json. */
+inline const Json& Json::get(size_type x) const {
+    ArrayJson *aj;
+    if (u_.x.type == j_array && (aj = ajson()) && unsigned(x) < unsigned(aj->size))
+        return aj->a[x];
+    else
+        return hard_get(x);
+}
+
+/** @brief Return a reference to the @a x th array element.
+
+    If this Json is an object, returns get_insert(String(x)). Otherwise this
+    Json is first converted to an array; non-arrays are converted to empty
+    arrays. The array is extended if @a x is out of range. */
+inline Json& Json::get_insert(size_type x) {
+    ArrayJson *aj;
+    if (u_.x.type == j_array && (aj = ajson()) && aj->refcount == 1
+        && unsigned(x) < unsigned(aj->size))
+        return aj->a[x];
+    else
+        return hard_get_insert(x);
+}
+
+/** @brief Return the @a x th element in an array Json.
+    @pre is_array()
+
+    A null Json is treated like an empty array. */
+inline const Json& Json::at(size_type x) const {
+    precondition(is_array());
+    return get(x);
+}
+
+/** @brief Return a reference to the @a x th element in an array Json.
+    @pre is_array()
+
+    The array is extended if @a x is out of range. */
+inline Json& Json::at_insert(size_type x) {
+    precondition(is_array());
+    return get_insert(x);
+}
+
+/** @brief Return the @a x th array element.
+
+    If @a x is out of range of this array, returns a null Json. If this is an
+    object Json, then returns get(String(@a x)). If this is neither an object
+    nor an array, returns a null Json. */
+inline const Json& Json::operator[](size_type x) const {
+    return get(x);
+}
+
+/** @brief Return a proxy reference to the @a x th array element.
+
+    If this Json is an object, returns operator[](String(x)). If this Json is
+    an array and @a x is in range, returns that element. Otherwise, returns a
+    proxy that acts like a null Json. If this proxy is assigned, this Json is
+    converted to an array, and then extended as necessary to contain the new
+    value. */
+inline Json_array_proxy<Json> Json::operator[](size_type x) {
+    return Json_array_proxy<Json>(*this, x);
+}
+
+/** @brief Return the last array element.
+    @pre is_array() && !empty() */
+inline const Json& Json::back() const {
+    precondition(is_array() && u_.a.x && u_.a.x->size > 0);
+    return u_.a.x->a[u_.a.x->size - 1];
+}
+
+/** @brief Return a reference to the last array element.
+    @pre is_array() && !empty() */
+inline Json& Json::back() {
+    precondition(is_array() && u_.a.x && u_.a.x->size > 0);
+    uniqueify_array(false, 0);
+    return u_.a.x->a[u_.a.x->size - 1];
+}
+
+/** @brief Push an element onto the back of the array.
+    @pre is_array() || is_null()
+    @return this Json
+
+    A null Json is promoted to an array. */
+inline Json& Json::push_back(Json x) {
+    new(uniqueify_array_insert(false, -1)) Json(std::move(x));
+    return *this;
+}
+
+/** @overload */
+template <typename P> inline Json& Json::push_back(const Json_proxy_base<P>& x) {
+    new(uniqueify_array_insert(false, -1)) Json(x.cvalue());
+    return *this;
+}
+
+/** @brief Remove the last element from an array.
+    @pre is_array() && !empty() */
+inline void Json::pop_back() {
+    precondition(is_array() && u_.a.x && u_.a.x->size > 0);
+    uniqueify_array(false, 0);
+    --u_.a.x->size;
+    u_.a.x->a[u_.a.x->size].~Json();
+}
+
+inline Json& Json::push_back_list() {
+    return *this;
+}
+
+/** @brief Insert the items [first, rest...] onto the back of this array.
+    @pre is_array() || is_null()
+    @return this Json
+
+    A null Json is promoted to an array. */
+template <typename T, typename... Args>
+inline Json& Json::push_back_list(T first, Args&&... rest) {
+    push_back(std::move(first));
+    push_back_list(std::forward<Args>(rest)...);
+    return *this;
+}
+
+
+/** @brief Insert an element into the array.
+    @pre is_array() || is_null()
+    @return this Json
+
+    A null Json is promoted to an array. */
+inline Json::array_iterator Json::insert(array_iterator position, Json x) {
+    precondition(position >= abegin() && position <= aend());
+    size_type pos = position - abegin();
+    new(uniqueify_array_insert(false, pos)) Json(std::move(x));
+    return abegin() + pos;
+}
+
+/** @overload */
+template <typename P> inline Json::array_iterator Json::insert(array_iterator position, const Json_proxy_base<P>& x) {
+    precondition(position >= abegin() && position <= aend());
+    size_type pos = position - abegin();
+    new(uniqueify_array_insert(false, pos)) Json(x.cvalue());
+    return abegin() + pos;
+}
+
+inline Json::array_iterator Json::erase(array_iterator position) {
+    return erase(position, position + 1);
+}
+
+
+inline Json* Json::array_data() {
+    precondition(is_null() || is_array());
+    uniqueify_array(false, 0);
+    return u_.a.x ? u_.a.x->a : 0;
+}
+
+inline const Json* Json::array_data() const {
+    precondition(is_null() || is_array());
+    return u_.a.x ? u_.a.x->a : 0;
+}
+
+inline const Json* Json::array_cdata() const {
+    precondition(is_null() || is_array());
+    return u_.a.x ? u_.a.x->a : 0;
+}
+
+inline Json* Json::end_array_data() {
+    precondition(is_null() || is_array());
+    uniqueify_array(false, 0);
+    return u_.a.x ? u_.a.x->a + u_.a.x->size : 0;
+}
+
+inline const Json* Json::end_array_data() const {
+    precondition(is_null() || is_array());
+    return u_.a.x ? u_.a.x->a + u_.a.x->size : 0;
+}
+
+inline const Json* Json::end_array_cdata() const {
+    precondition(is_null() || is_array());
+    return u_.a.x ? u_.a.x->a + u_.a.x->size : 0;
+}
+
+
+inline Json::const_object_iterator Json::cobegin() const {
+    precondition(is_null() || is_object());
+    return const_object_iterator(this, 0);
+}
+
+inline Json::const_object_iterator Json::coend() const {
+    precondition(is_null() || is_object());
+    return const_object_iterator(this, -1);
+}
+
+inline Json::const_object_iterator Json::obegin() const {
+    return cobegin();
+}
+
+inline Json::const_object_iterator Json::oend() const {
+    return coend();
+}
+
+inline Json::object_iterator Json::obegin() {
+    precondition(is_null() || is_object());
+    return object_iterator(this, 0);
+}
+
+inline Json::object_iterator Json::oend() {
+    precondition(is_null() || is_object());
+    return object_iterator(this, -1);
+}
+
+inline Json::const_array_iterator Json::cabegin() const {
+    precondition(is_null() || is_array());
+    return const_array_iterator(this, 0);
+}
+
+inline Json::const_array_iterator Json::caend() const {
+    precondition(is_null() || is_array());
+    ArrayJson *aj = ajson();
+    return const_array_iterator(this, aj ? aj->size : 0);
+}
+
+inline Json::const_array_iterator Json::abegin() const {
+    return cabegin();
+}
+
+inline Json::const_array_iterator Json::aend() const {
+    return caend();
+}
+
+inline Json::array_iterator Json::abegin() {
+    precondition(is_null() || is_array());
+    return array_iterator(this, 0);
+}
+
+inline Json::array_iterator Json::aend() {
+    precondition(is_null() || is_array());
+    ArrayJson *aj = ajson();
+    return array_iterator(this, aj ? aj->size : 0);
+}
+
+inline Json::const_iterator Json::cbegin() const {
+    return const_iterator(this, 0);
+}
+
+inline Json::const_iterator Json::cend() const {
+    return const_iterator(this, -1);
+}
+
+inline Json::iterator Json::begin() {
+    return iterator(this, 0);
+}
+
+inline Json::iterator Json::end() {
+    return iterator(this, -1);
+}
+
+inline Json::const_iterator Json::begin() const {
+    return cbegin();
+}
+
+inline Json::const_iterator Json::end() const {
+    return cend();
+}
+
+
+// Unparsing
+class Json::unparse_manipulator {
+  public:
+    unparse_manipulator()
+        : indent_depth_(0), tab_width_(0), newline_terminator_(false),
+          space_separator_(false) {
+    }
+    int indent_depth() const {
+        return indent_depth_;
+    }
+    unparse_manipulator indent_depth(int x) const {
+        unparse_manipulator m(*this);
+        m.indent_depth_ = x;
+        return m;
+    }
+    int tab_width() const {
+        return tab_width_;
+    }
+    unparse_manipulator tab_width(int x) const {
+        unparse_manipulator m(*this);
+        m.tab_width_ = x;
+        return m;
+    }
+    bool newline_terminator() const {
+        return newline_terminator_;
+    }
+    unparse_manipulator newline_terminator(bool x) const {
+        unparse_manipulator m(*this);
+        m.newline_terminator_ = x;
+        return m;
+    }
+    bool space_separator() const {
+        return space_separator_;
+    }
+    unparse_manipulator space_separator(bool x) const {
+        unparse_manipulator m(*this);
+        m.space_separator_ = x;
+        return m;
+    }
+    bool empty() const {
+        return !indent_depth_ && !newline_terminator_ && !space_separator_;
+    }
+  private:
+    int indent_depth_;
+    int tab_width_;
+    bool newline_terminator_;
+    bool space_separator_;
+};
+
+inline Json::unparse_manipulator Json::indent_depth(int x) {
+    return unparse_manipulator().indent_depth(x);
+}
+inline Json::unparse_manipulator Json::tab_width(int x) {
+    return unparse_manipulator().tab_width(x);
+}
+inline Json::unparse_manipulator Json::newline_terminator(bool x) {
+    return unparse_manipulator().newline_terminator(x);
+}
+inline Json::unparse_manipulator Json::space_separator(bool x) {
+    return unparse_manipulator().space_separator(x);
+}
+
+/** @brief Return the string representation of this Json. */
+inline String Json::unparse() const {
+    StringAccum sa;
+    hard_unparse(sa, default_manipulator, 0);
+    return sa.take_string();
+}
+
+/** @brief Return the string representation of this Json.
+    @param add_newline If true, add a final newline. */
+inline String Json::unparse(const unparse_manipulator &m) const {
+    StringAccum sa;
+    hard_unparse(sa, m, 0);
+    return sa.take_string();
+}
+
+/** @brief Unparse the string representation of this Json into @a sa. */
+inline void Json::unparse(StringAccum &sa) const {
+    hard_unparse(sa, default_manipulator, 0);
+}
+
+/** @brief Unparse the string representation of this Json into @a sa. */
+inline void Json::unparse(StringAccum &sa, const unparse_manipulator &m) const {
+    hard_unparse(sa, m, 0);
+}
+
+
+// Parsing
+
+class Json::streaming_parser {
+  public:
+    inline streaming_parser();
+    inline void reset();
+
+    inline bool done() const;
+    inline bool success() const;
+    inline bool error() const;
+
+    inline size_t consume(const char* first, size_t length,
+                          const String& str = String(),
+                          bool complete = false);
+    inline const char* consume(const char* first, const char* last,
+                               const String& str = String(),
+                               bool complete = false);
+    const uint8_t* consume(const uint8_t* first, const uint8_t* last,
+                           const String& str = String(),
+                           bool complete = false);
+
+    inline Json& result();
+    inline const Json& result() const;
+
+  private:
+    enum {
+        st_final = -2, st_error = -1,
+        st_partlenmask = 0x0F, st_partmask = 0xFF,
+        st_stringpart = 0x10, st_primitivepart = 0x20, st_numberpart = 0x40,
+
+        st_initial = 0x000,
+        st_array_initial = 0x100,
+        st_array_value = 0x200,
+        st_object_value = 0x300,
+
+        st_array_delim = 0x400,
+        st_object_delim = 0x500,
+        st_object_initial = 0x600,
+        st_object_key = 0x700,
+        st_object_colon = 0x800,
+
+        max_depth = 2048
+    };
+
+    int state_;
+    std::vector<Json*> stack_;
+    String str_;
+    Json json_;
+
+    inline Json* current();
+    const uint8_t* error_at(const uint8_t* here);
+    const uint8_t* consume_string(const uint8_t* first, const uint8_t* last, const String& str);
+    const uint8_t* consume_backslash(StringAccum& sa, const uint8_t* first, const uint8_t* last);
+    const uint8_t* consume_stringpart(StringAccum& sa, const uint8_t* first, const uint8_t* last);
+    const uint8_t* consume_primitive(const uint8_t* first, const uint8_t* last, Json& j);
+    const uint8_t* consume_number(const uint8_t* first, const uint8_t* last, const String& str, bool complete, Json& j);
+};
+
+inline Json::streaming_parser::streaming_parser()
+    : state_(st_initial) {
+}
+
+inline void Json::streaming_parser::reset() {
+    state_ = st_initial;
+    stack_.clear();
+}
+
+inline bool Json::streaming_parser::done() const {
+    return state_ < 0;
+}
+
+inline bool Json::streaming_parser::success() const {
+    return state_ == st_final;
+}
+
+inline bool Json::streaming_parser::error() const {
+    return state_ == st_error;
+}
+
+inline size_t Json::streaming_parser::consume(const char* first, size_t length,
+                                              const String& str,
+                                              bool complete) {
+    const uint8_t* ufirst = reinterpret_cast<const uint8_t*>(first);
+    return consume(ufirst, ufirst + length, str, complete) - ufirst;
+}
+
+inline const char* Json::streaming_parser::consume(const char* first,
+                                                   const char* last,
+                                                   const String& str,
+                                                   bool complete) {
+    return reinterpret_cast<const char*>
+        (consume(reinterpret_cast<const uint8_t*>(first),
+                 reinterpret_cast<const uint8_t*>(last), str, complete));
+}
+
+inline Json& Json::streaming_parser::result() {
+    return json_;
+}
+
+inline const Json& Json::streaming_parser::result() const {
+    return json_;
+}
+
+
+/** @brief Parse @a str as UTF-8 JSON into this Json object.
+    @return true iff the parse succeeded.
+
+    An unsuccessful parse does not modify *this. */
+inline bool Json::assign_parse(const String &str) {
+    return assign_parse(str.begin(), str.end(), str);
+}
+
+/** @brief Parse [@a first, @a last) as UTF-8 JSON into this Json object.
+    @return true iff the parse succeeded.
+
+    An unsuccessful parse does not modify *this. */
+inline bool Json::assign_parse(const char *first, const char *last) {
+    return assign_parse(first, last, String());
+}
+
+/** @brief Return @a str parsed as UTF-8 JSON.
+
+    Returns a null JSON object if the parse fails. */
+inline Json Json::parse(const String &str) {
+    Json j;
+    (void) j.assign_parse(str);
+    return j;
+}
+
+/** @brief Return [@a first, @a last) parsed as UTF-8 JSON.
+
+    Returns a null JSON object if the parse fails. */
+inline Json Json::parse(const char *first, const char *last) {
+    Json j;
+    (void) j.assign_parse(first, last);
+    return j;
+}
+
+
+// Assignment
+
+inline Json& Json::operator=(const Json& x) {
+    if (x.u_.x.type < 0)
+        x.u_.str.ref();
+    else if (x.u_.x.x && (x.u_.x.type == j_array || x.u_.x.type == j_object))
+        x.u_.x.x->ref();
+    deref();
+    u_ = x.u_;
+    return *this;
+}
+
+#if HAVE_CXX_RVALUE_REFERENCES
+inline Json& Json::operator=(Json&& x) {
+    using std::swap;
+    swap(u_, x.u_);
+    return *this;
+}
+#endif
+
+/** @cond never */
+template <typename U>
+inline Json& Json::operator=(const Json_proxy_base<U> &x) {
+    return *this = x.cvalue();
+}
+
+inline Json& Json::operator=(int x) {
+    deref();
+    u_.i.x = x;
+    u_.i.type = j_int;
+    return *this;
+}
+
+inline Json& Json::operator=(unsigned x) {
+    deref();
+    u_.u.x = x;
+    u_.u.type = j_unsigned;
+    return *this;
+}
+
+inline Json& Json::operator=(long x) {
+    deref();
+    u_.i.x = x;
+    u_.i.type = j_int;
+    return *this;
+}
+
+inline Json& Json::operator=(unsigned long x) {
+    deref();
+    u_.u.x = x;
+    u_.u.type = j_unsigned;
+    return *this;
+}
+
+inline Json& Json::operator=(long long x) {
+    deref();
+    u_.i.x = x;
+    u_.i.type = j_int;
+    return *this;
+}
+
+inline Json& Json::operator=(unsigned long long x) {
+    deref();
+    u_.u.x = x;
+    u_.u.type = j_unsigned;
+    return *this;
+}
+
+inline Json& Json::operator=(double x) {
+    deref();
+    u_.d.x = x;
+    u_.d.type = j_double;
+    return *this;
+}
+
+inline Json& Json::operator=(bool x) {
+    deref();
+    u_.i.x = x;
+    u_.i.type = j_bool;
+    return *this;
+}
+
+inline Json& Json::operator=(const String& x) {
+    deref();
+    u_.str = x.internal_rep();
+    u_.str.ref();
+    return *this;
+}
+/** @endcond never */
+
+inline Json& Json::operator++() {
+    return *this += 1;
+}
+inline void Json::operator++(int) {
+    ++(*this);
+}
+inline Json& Json::operator--() {
+    return *this += -1;
+}
+inline void Json::operator--(int) {
+    --(*this);
+}
+inline Json& Json::add(double x) {
+    force_double();
+    u_.d.x += x;
+    return *this;
+}
+template <typename T>
+inline Json& Json::add(T x) {
+    force_number();
+    if (is_int())
+        u_.u.x += x;
+    else
+        u_.d.x += x;
+    return *this;
+}
+inline Json& Json::subtract(double x) {
+    force_double();
+    u_.d.x -= x;
+    return *this;
+}
+template <typename T>
+inline Json& Json::subtract(T x) {
+    force_number();
+    if (is_int())
+        u_.u.x -= x;
+    else
+        u_.d.x -= x;
+    return *this;
+}
+inline Json& Json::operator+=(int x) {
+    return add(x);
+}
+inline Json& Json::operator+=(unsigned x) {
+    return add(x);
+}
+inline Json& Json::operator+=(long x) {
+    return add(x);
+}
+inline Json& Json::operator+=(unsigned long x) {
+    return add(x);
+}
+inline Json& Json::operator+=(long long x) {
+    return add(x);
+}
+inline Json& Json::operator+=(unsigned long long x) {
+    return add(x);
+}
+inline Json& Json::operator+=(double x) {
+    return add(x);
+}
+inline Json& Json::operator-=(int x) {
+    return subtract(x);
+}
+inline Json& Json::operator-=(unsigned x) {
+    return subtract(x);
+}
+inline Json& Json::operator-=(long x) {
+    return subtract(x);
+}
+inline Json& Json::operator-=(unsigned long x) {
+    return subtract(x);
+}
+inline Json& Json::operator-=(long long x) {
+    return subtract(x);
+}
+inline Json& Json::operator-=(unsigned long long x) {
+    return subtract(x);
+}
+inline Json& Json::operator-=(double x) {
+    return subtract(x);
+}
+inline Json& Json::operator+=(const Json& x) {
+    if (!x.is_null()) {
+        // XXX what if both are integers
+        force_number();
+        u_.d.x = as_d() + x.as_d();
+        u_.d.type = j_double;
+    }
+    return *this;
+}
+inline Json& Json::operator-=(const Json& x) {
+    if (!x.is_null()) {
+        // XXX what if both are integers
+        force_number();
+        u_.d.x = as_d() - x.as_d();
+        u_.d.type = j_double;
+    }
+    return *this;
+}
+inline Json operator+(Json x) {
+    x.force_number();
+    return x;
+}
+inline Json operator-(Json x) {
+    x.force_number();
+    if (x.is_int())
+        x.u_.u.x = -x.u_.u.x;
+    else
+        x.u_.d.x = -x.u_.d.x;
+    return x;
+}
+
+/** @brief Swap this Json with @a x. */
+inline void Json::swap(Json& x) {
+    using std::swap;
+    swap(u_, x.u_);
+}
+
+
+inline StringAccum &operator<<(StringAccum &sa, const Json& json) {
+    json.unparse(sa);
+    return sa;
+}
+
+template <typename P>
+inline StringAccum &operator<<(StringAccum &sa, const Json_proxy_base<P> &json) {
+    return (sa << json.cvalue());
+}
+
+inline std::ostream &operator<<(std::ostream& f, const Json& json) {
+    StringAccum sa;
+    json.unparse(sa);
+    return f.write(sa.data(), sa.length());
+}
+
+template <typename P>
+inline std::ostream &operator<<(std::ostream& f, const Json_proxy_base<P>& json) {
+    return (f << json.cvalue());
+}
+
+bool operator==(const Json& a, const Json& b);
+
+template <typename T>
+inline bool operator==(const Json_proxy_base<T>& a, const Json& b) {
+    return a.cvalue() == b;
+}
+
+template <typename T>
+inline bool operator==(const Json& a, const Json_proxy_base<T>& b) {
+    return a == b.cvalue();
+}
+
+template <typename T, typename U>
+inline bool operator==(const Json_proxy_base<T>& a,
+                       const Json_proxy_base<U>& b) {
+    return a.cvalue() == b.cvalue();
+}
+
+inline bool operator!=(const Json& a, const Json& b) {
+    return !(a == b);
+}
+
+template <typename T>
+inline bool operator!=(const Json_proxy_base<T>& a, const Json& b) {
+    return !(a == b);
+}
+
+template <typename T>
+inline bool operator!=(const Json& a, const Json_proxy_base<T>& b) {
+    return !(a == b);
+}
+
+template <typename T, typename U>
+inline bool operator!=(const Json_proxy_base<T>& a,
+                       const Json_proxy_base<U>& b) {
+    return !(a == b);
+}
+
+inline void swap(Json& a, Json& b) {
+    a.swap(b);
+}
+
+} // namespace lcdf
+#endif
diff --git a/silo/masstree/jsontest.cc b/silo/masstree/jsontest.cc
new file mode 100644 (file)
index 0000000..e579939
--- /dev/null
@@ -0,0 +1,603 @@
+// -*- c-basic-offset: 4 -*-
+/*
+ * jsontest.{cc,hh} -- regression tests for Json
+ * Eddie Kohler
+ *
+ * Copyright (c) 2012-2014 Eddie Kohler
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file is
+ * legally binding.
+ */
+
+#include "json.hh"
+#include <unordered_map>
+using namespace lcdf;
+
+#define CHECK(x) do { if (!(x)) { std::cerr << __FILE__ << ":" << __LINE__ << ": test '" << #x << "' failed\n"; exit(1); } } while (0)
+#define CHECK_JUP(x, str) do { if ((x).unparse() != (str)) { std::cerr << __FILE__ << ":" << __LINE__ << ": '" #x "' is '" << (x) << "', not '" << (str) << "'\n"; exit(1); } } while (0)
+
+
+#if 0
+template <typename T> void incr(T& x) __attribute__((noinline));
+template <typename T>
+void incr(T& x) {
+    ++x;
+}
+#endif
+
+void benchmark_parse() {
+    std::vector<String> parse_examples;
+    parse_examples.push_back("{}");
+    parse_examples.push_back("{\"foo\":\"bar\",\"baz\":\"flim\"}");
+    parse_examples.push_back("[1,2,3,4,5]");
+    parse_examples.push_back("[]");
+    parse_examples.push_back("[{},{\"b\":[]}]");
+    Json j;
+#if 0
+    for (int i = 0; i < 10000000; ++i)
+        j.assign_parse(parse_examples[rand() % parse_examples.size()]);
+#else
+    using std::swap;
+    Json::streaming_parser jsp;
+    for (int i = 0; i < 10000000; ++i) {
+        jsp.reset();
+        swap(jsp.result(), j);
+        const String& str = parse_examples[rand() % parse_examples.size()];
+        jsp.consume(str.begin(), str.end(), str, true);
+    }
+#endif
+    exit(0);
+}
+
+int main(int argc, char** argv) {
+    (void) argc, (void) argv;
+    //benchmark_parse();
+
+    Json j;
+    CHECK(j.empty());
+    CHECK(!j);
+
+    j = Json::make_object();
+    CHECK(j.empty());
+    CHECK(j);
+
+    j.set("foo", "bar");
+    CHECK(j["foo"]);
+    CHECK(j["foo"].to_s() == "bar");
+    CHECK(j.size() == 1);
+
+    j.set("baz", "flim");
+    CHECK(j.size() == 2);
+    CHECK(j.unparse() == "{\"foo\":\"bar\",\"baz\":\"flim\"}");
+
+    j.erase("foo");
+    CHECK(j.size() == 1);
+
+    j.assign_parse("2");
+    CHECK(j == 2);
+
+    j.assign_parse("null");
+    CHECK(j == Json());
+
+    j.assign_parse("\"a\"");
+    CHECK(j == "a");
+
+    j.assign_parse("[1,2]");
+    CHECK(j.unparse() == "[1,2]");
+
+    j.assign_parse("[[[]],{\"a\":{}}]");
+    CHECK(j.unparse() == "[[[]],{\"a\":{}}]");
+
+    j = Json::parse("{\"x22\":{\n\
+      \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
+      \"time\":\"Tue Feb  7 20:20:33 2012\",\n\
+      \"machine\":\"rtshanks-laptop\",\n\
+      \"cores\":2,\n\
+      \"runs\":[\"x22\\/rw2\\/mb\\/0\"]\n\
+    },\n\
+    \"x23\":{\n\
+      \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
+      \"time\":\"Tue Feb  7 20:31:05 2012\",\n\
+      \"machine\":\"rtshanks-laptop\",\n\
+      \"cores\":2,\n\
+      \"runs\":[\"x23\\/rw2\\/mb\\/0\",\"x23\\/rw1\\/mb\\/0\",\"x23\\/rw3\\/mb\\/0\"]\n\
+    },\n\
+    \"x24\":{\n\
+      \"git-revision\":\"62e9970ca8ae9c6eebf2d71b7065ea694fb25282M\",\n\
+      \"time\":\"Sat Feb 11 15:54:01 2012\",\n\
+      \"machine\":\"rtshanks-laptop\",\n\
+      \"cores\":2,\n\
+      \"runs\":[\"x24\\/rw1\\/b\\/0\"]\n\
+    },\"b\":\"c\",\"c\":\"d\"}");
+    CHECK(j["x22"]["time"] == "Tue Feb  7 20:20:33 2012");
+    CHECK(j["x22"]["cores"] == 2);
+    {
+        Json::object_iterator it = j.obegin();
+        CHECK(it.key() == "x22");
+        ++it;
+        CHECK(it.key() == "x23");
+        ++it;
+        CHECK(it.key() == "x24");
+        ++it;
+        CHECK(it.key() == "b");
+        ++it;
+        CHECK(it.key() == "c");
+    }
+
+    {
+        Json jcopy = j;
+        CHECK(j.size() == 5);
+        int count = 0;
+        for (Json::iterator it = jcopy.begin(); it != jcopy.end(); ++it) {
+            it->second = Json();
+            ++count;
+        }
+        CHECK(!jcopy["x22"]);
+        CHECK(j["x22"]["cores"] == 2);
+        CHECK(count == jcopy.size());
+    }
+
+    CHECK(!j["x49"]);
+    CHECK(j.size() == 5);
+    CHECK(!j["x49"]["45"][2]["a"]);
+    CHECK(j.size() == 5);
+    j["x49"]["45"][2]["a"] = 1;
+    CHECK(j.size() == 6);
+    CHECK(j["x22"].is_object() && j.get("x22").is_object());
+    CHECK(j["x23"].is_object() && j.get("x23").is_object());
+    CHECK(j["b"].is_string() && j.get("b").is_string());
+    CHECK(j["x49"].is_object() && j.get("x49").is_object());
+    CHECK(j["x49"]["45"].is_array());
+    CHECK(j["x49"]["45"].size() == 3 && j["x49"]["45"][2].is_object());
+
+    j = Json::make_object();
+    j["a"] = j["b"];
+    CHECK(j.size() == 1);
+    CHECK(j["a"].is_null());
+    CHECK(j.count("a") == 1);
+
+    Json k = Json::make_array();
+    {
+        CHECK(k.size() == 0);
+        Json::array_iterator it = k.abegin();
+        CHECK(!it.live());
+    }
+    j = Json::make_object();
+    j["a"] = k[2];
+    CHECK(j.size() == 1);
+    CHECK(k.size() == 0);
+    CHECK(j["a"].is_null());
+    CHECK(j.count("a") == 1);
+
+    j = Json(1);
+    CHECK(j.get("a").is_null());
+
+    j.assign_parse("{\"a\":1,\"b\":true,\"c\":\"\"}");
+    {
+        int i = 0;
+        bool b = false;
+        String s;
+        CHECK(j.get("a", i));
+        CHECK(i == 1);
+        CHECK(j.get("a", i).get("b", b));
+        CHECK(b == true);
+        CHECK(!j.get("a", s).status());
+        CHECK(!j.get("a", s).status(b));
+        CHECK(b == false);
+        CHECK(j.get("a", k));
+        CHECK(k == Json(1));
+        CHECK(!j.get("cc", k));
+    }
+
+    j["a"] = Json(5);
+    j.set("a", Json(5));
+    j["b"] = Json::parse("[]");
+
+    {
+        Json j1 = Json::make_object(), j2 = Json::make_object();
+        j1.set("a", j2); // stores a COPY of j2 in j1
+        j2.set("b", 1);
+        CHECK(j1.unparse() == "{\"a\":{}}");
+        CHECK(j2.unparse() == "{\"b\":1}");
+    }
+
+    {
+        Json j = Json::parse("{\"a\":true}");
+        if (j["foo"]["bar"])
+            CHECK(false);
+        CHECK(j.unparse() == "{\"a\":true}");
+        j["foo"]["bar"] = true;
+        CHECK(j.unparse() == "{\"a\":true,\"foo\":{\"bar\":true}}");
+        j["a"]["2"] = false;
+        CHECK(j.unparse() == "{\"a\":{\"2\":false},\"foo\":{\"bar\":true}}");
+        j[3] = true;
+        CHECK(j.unparse() == "{\"a\":{\"2\":false},\"foo\":{\"bar\":true},\"3\":true}");
+        CHECK(j[3] == Json(true));
+        j = Json::parse("[1,2,3,4]");
+        CHECK(j["2"] == Json(3));
+        CHECK(j.unparse() == "[1,2,3,4]");
+        j["a"] = true;
+        CHECK(j.unparse() == "{\"0\":1,\"1\":2,\"2\":3,\"3\":4,\"a\":true}");
+        CHECK(j["2"] == Json(3));
+    }
+
+    {
+        Json j = Json::parse("{}");
+        j.set("foo", String::make_out_of_memory()).set(String::make_out_of_memory(), 2);
+        CHECK(j.unparse() == "{\"foo\":\"\360\237\222\243ENOMEM\360\237\222\243\",\"\360\237\222\243ENOMEM\360\237\222\243\":2}");
+        j.clear();
+        CHECK(j.unparse() == "{}");
+        CHECK(j.get("foo") == Json());
+    }
+
+    {
+        Json j = Json::array(1, 2, 3, 4, 5, 6, 7, 8);
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
+        Json jcopy = j;
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
+        CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8]");
+        j.push_back(9);
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8,9]");
+        CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8]");
+        jcopy.push_back(10);
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8,9]");
+        CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8,10]");
+    }
+
+    {
+        unsigned long s = 77;
+        Json j = Json::array(0, 0, 0);
+        Json k = Json::array(0, s, "foo");
+        CHECK(j[1].is_i());
+        CHECK(k[1].is_u());
+        j[1] = k[1];
+        CHECK(j[1].is_u());
+    }
+
+#if 0
+    {
+        Json j = Json::array(1, 2, 3);
+        //int j[3] = {1, 2, 3};
+        for (int i = 0; i < 1000000; ++i)
+            incr(j[1].value());
+        std::cout << j << "\n";
+    }
+#endif
+
+    {
+        Json::streaming_parser jsp;
+        const uint8_t* x = reinterpret_cast<const uint8_t*>("\"abcdef\"");
+
+        const uint8_t* r = jsp.consume(x, x + 8, String());
+        CHECK(r == x + 8);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"abcdef\"");
+
+        x = reinterpret_cast<const uint8_t*>("\"\\\"\\\\fartbox\" amksdnsndfa");
+        jsp.reset();
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 9);
+        CHECK(!jsp.done());
+        r = jsp.consume(x + 9, x + 17, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\\\"\\\\fartbox\"");
+
+        jsp.reset();
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\\\"\\\\fartbox\"");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("\"\\uD834\\uDD1E\"f");
+        r = jsp.consume(x, x + 15, String());
+        CHECK(r == x + 14);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
+
+        jsp.reset();
+        r = jsp.consume(x, x + 2, String());
+        CHECK(r == x + 2);
+        r = jsp.consume(x + 2, x + 4, String());
+        CHECK(r == x + 4);
+        r = jsp.consume(x + 4, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 8, String());
+        CHECK(r == x + 8);
+        r = jsp.consume(x + 8, x + 10, String());
+        CHECK(r == x + 10);
+        r = jsp.consume(x + 10, x + 15, String());
+        CHECK(r == x + 14);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
+
+        x = reinterpret_cast<const uint8_t*>("\"\xF0\x9D\x84\x9E\" fart");
+        jsp.reset();
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 5, x + 7, String());
+        CHECK(r == x + 5);
+        CHECK(jsp.error());
+
+        jsp.reset();
+        r = jsp.consume(x, x + 7, String());
+        CHECK(r == x + 6);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
+
+        jsp.reset();
+        r = jsp.consume(x, x + 2, String());
+        CHECK(r == x + 2);
+        r = jsp.consume(x + 2, x + 4, String());
+        CHECK(r == x + 4);
+        r = jsp.consume(x + 4, x + 7, String());
+        CHECK(r == x + 6);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{}");
+        r = jsp.consume(x, x + 2, String());
+        CHECK(r == x + 2);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("\"ab cde\" ");
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 8);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "\"ab cde\"");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"a\":\"b\"}");
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 9);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"a\":\"b\"}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[]");
+        r = jsp.consume(x, x + 2, String());
+        CHECK(r == x + 2);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[]");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[\"a\",\"b\"]");
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 9);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[\"a\",\"b\"]");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[[\"a\"],[[\"b\"]]]");
+        r = jsp.consume(x, x + 15, String());
+        CHECK(r == x + 15);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[[\"a\"],[[\"b\"]]]");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[[],[[]]]");
+        r = jsp.consume(x, x + 9, String());
+        CHECK(r == x + 9);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[[],[[]]]");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\":\"def\"}");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":\"def\"}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\":true }");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":true}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\": null}");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":null}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\":false}");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":false}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\": -13 }");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":-13}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("{\"abc\":0    }");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 13, String());
+        CHECK(r == x + 13);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "{\"abc\":0}");
+
+        jsp.reset();
+        x = reinterpret_cast<const uint8_t*>("[0,1,2,3,4e5,10.2]");
+        r = jsp.consume(x, x + 3, String());
+        CHECK(r == x + 3);
+        r = jsp.consume(x + 3, x + 6, String());
+        CHECK(r == x + 6);
+        r = jsp.consume(x + 6, x + 9, String());
+        CHECK(r == x + 9);
+        r = jsp.consume(x + 9, x + 12, String());
+        CHECK(r == x + 12);
+        r = jsp.consume(x + 12, x + 18, String());
+        CHECK(r == x + 18);
+        CHECK(jsp.success());
+        CHECK(jsp.result().unparse() == "[0,1,2,3,400000,10.2]");
+    }
+
+    {
+        unsigned long s = 77;
+        Json j = Json::array(0, s, "foo");
+        CHECK(j.is_a());
+        CHECK(j[1].is_u());
+        CHECK(j[1] == s);
+        CHECK(j[1] == 77);
+
+        j = Json::array((uint64_t) -1, (int64_t) -1,
+                        (uint64_t) 1, (int64_t) 1,
+                        (uint64_t) 2, (int64_t) 2);
+        CHECK(j[0] != j[1]);
+        CHECK(j[2] == j[3]);
+        CHECK(j[4] == j[5]);
+        CHECK(j[0] != j[2]);
+        CHECK(j[2] != j[4]);
+        CHECK_JUP(j, "[18446744073709551615,-1,1,1,2,2]");
+    }
+
+    {
+        std::vector<int> v = {1, 2, 3, 4, 5};
+        Json j(v.begin(), v.end());
+        CHECK(j.unparse() == "[1,2,3,4,5]");
+    }
+
+    {
+        std::unordered_map<String, String> h;
+        h["a"] = "b";
+        h["c"] = "d";
+        h["x"] = "e";
+        Json j(h.begin(), h.end());
+        CHECK(j.is_o());
+        CHECK(j.size() == 3);
+        CHECK(j["a"] == "b");
+        CHECK(j["c"] == "d");
+        CHECK(j["x"] == "e");
+    }
+
+    {
+        Json j = Json::array(1, 2, 3, 4, 5, 6, 7, 8);
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
+        Json jcopy = j;
+        CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
+        CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8]");
+        auto it = j.erase(j.abegin() + 4);
+        CHECK_JUP(j, "[1,2,3,4,6,7,8]");
+        CHECK_JUP(jcopy, "[1,2,3,4,5,6,7,8]");
+        CHECK(it == j.abegin() + 4);
+        it = j.erase(j.aend(), j.aend());
+        CHECK_JUP(j, "[1,2,3,4,6,7,8]");
+        CHECK(it == j.aend());
+        it = j.erase(j.abegin(), j.abegin());
+        CHECK_JUP(j, "[1,2,3,4,6,7,8]");
+        CHECK(it == j.abegin());
+        it = j.erase(j.abegin(), j.abegin() + 3);
+        CHECK_JUP(j, "[4,6,7,8]");
+        CHECK(it == j.abegin());
+    }
+
+    {
+        Json j((uint64_t) 1 << 63);
+        CHECK(j.is_u());
+        CHECK(j.unparse() == "9223372036854775808");
+        j = Json::parse("9223372036854775808");
+        CHECK(j.is_u());
+        CHECK(j.to_u() == (uint64_t) 1 << 63);
+    }
+
+    {
+        Json j = Json::object("a", 1, "b", Json(2), "c", "9137471");
+        CHECK(j.unparse() == "{\"a\":1,\"b\":2,\"c\":\"9137471\"}");
+    }
+
+    {
+        Json a = Json::parse("[{\"a\":1},{\"b\":2},{\"c\":3},{\"d\":4}]");
+        Json b = Json::array(a[1], a[3], a[2], a[0]);
+        CHECK(&a[0]["a"].cvalue() == &b[3]["a"].cvalue());
+        b.push_back(a[5]);
+        CHECK(b[4] == Json());
+        CHECK(a.size() == 4);
+        b = Json::object("a5", a[5], "a3", a[3]);
+        CHECK(b.unparse() == "{\"a5\":null,\"a3\":{\"d\":4}}");
+        CHECK(a.size() == 4);
+        b.set_list("a6", a[6], "a2", a[2]);
+        CHECK(b.unparse() == "{\"a5\":null,\"a3\":{\"d\":4},\"a6\":null,\"a2\":{\"c\":3}}");
+        CHECK(a.size() == 4);
+    }
+
+    {
+        Json a = Json::parse("[{\"a\":\"\\\"\\\\\\/\"}]");
+        CHECK(a[0]["a"] == "\"\\/");
+        CHECK(a.unparse() == "[{\"a\":\"\\\"\\\\\\/\"}]");
+    }
+
+    std::cout << "All tests pass!\n";
+    return 0;
+}
diff --git a/silo/masstree/kpermuter.hh b/silo/masstree/kpermuter.hh
new file mode 100644 (file)
index 0000000..6a1ebba
--- /dev/null
@@ -0,0 +1,325 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KPERMUTER_HH
+#define KPERMUTER_HH
+#include "string.hh"
+
+class identity_kpermuter {
+    int size_;
+  public:
+    identity_kpermuter(int size)
+        : size_(size) {
+    }
+
+    int size() const {
+        return size_;
+    }
+    int operator[](int i) const {
+        return i;
+    }
+    bool operator==(const identity_kpermuter&) const {
+        return true;
+    }
+    bool operator!=(const identity_kpermuter&) const {
+        return false;
+    }
+};
+
+template <int C> struct sized_kpermuter_info {};
+template <> struct sized_kpermuter_info<0> {
+    typedef uint16_t storage_type;
+    typedef unsigned value_type;
+    enum { initial_value = 0x0120U, full_value = 0x2100U };
+};
+template <> struct sized_kpermuter_info<1> {
+    typedef uint32_t storage_type;
+    typedef unsigned value_type;
+    enum { initial_value = 0x01234560U, full_value = 0x65432100U };
+};
+template <> struct sized_kpermuter_info<2> {
+    typedef uint64_t storage_type;
+    typedef uint64_t value_type;
+    enum { initial_value = (uint64_t) 0x0123456789ABCDE0ULL,
+           full_value = (uint64_t) 0xEDCBA98765432100ULL };
+};
+
+template <int width> class kpermuter {
+  public:
+    typedef sized_kpermuter_info<(width > 3) + (width > 7) + (width > 15)> info;
+    typedef typename info::storage_type storage_type;
+    typedef typename info::value_type value_type;
+    enum { max_width = (int) (sizeof(storage_type) * 2 - 1) };
+    enum { size_bits = 4 };
+
+    /** @brief Construct an uninitialized permuter. */
+    kpermuter() {
+    }
+    /** @brief Construct a permuter with value @a x. */
+    kpermuter(value_type x)
+        : x_(x) {
+    }
+
+    /** @brief Return an empty permuter with size 0.
+
+        Elements will be allocated in order 0, 1, ..., @a width - 1. */
+    static inline value_type make_empty() {
+        value_type p = (value_type) info::initial_value >> ((max_width - width) << 2);
+        return p & ~(value_type) 15;
+    }
+    /** @brief Return a permuter with size @a n.
+
+        The returned permutation has size() @a n. For 0 <= i < @a n,
+        (*this)[i] == i. Elements n through @a width - 1 are free, and will be
+        allocated in that order. */
+    static inline value_type make_sorted(int n) {
+        value_type mask = (n == width ? (value_type) 0 : (value_type) 16 << (n << 2)) - 1;
+        return (make_empty() << (n << 2))
+            | ((value_type) info::full_value & mask)
+            | n;
+    }
+
+    /** @brief Return the permuter's size. */
+    int size() const {
+        return x_ & 15;
+    }
+    /** @brief Return the permuter's element @a i.
+        @pre 0 <= i < width */
+    int operator[](int i) const {
+        return (x_ >> ((i << 2) + 4)) & 15;
+    }
+    int back() const {
+        return (*this)[width - 1];
+    }
+    value_type value() const {
+        return x_;
+    }
+    value_type value_from(int i) const {
+        return x_ >> ((i + 1) << 2);
+    }
+
+    void set_size(int n) {
+        x_ = (x_ & ~(value_type) 15) | n;
+    }
+    /** @brief Allocate a new element and insert it at position @a i.
+        @pre 0 <= @a i < @a width
+        @pre size() < @a width
+        @return The newly allocated element.
+
+        Consider the following code:
+        <code>
+        kpermuter<...> p = ..., q = p;
+        int x = q.insert_from_back(i);
+        </code>
+
+        The modified permuter, q, has the following properties.
+        <ul>
+        <li>q.size() == p.size() + 1</li>
+        <li>Given j with 0 <= j < i, q[j] == p[j] && q[j] != x</li>
+        <li>Given j with j == i, q[j] == x</li>
+        <li>Given j with i < j < q.size(), q[j] == p[j-1] && q[j] != x</li>
+        </ul> */
+    int insert_from_back(int i) {
+        int value = back();
+        // increment size, leave lower slots unchanged
+        x_ = ((x_ + 1) & (((value_type) 16 << (i << 2)) - 1))
+            // insert slot
+            | ((value_type) value << ((i << 2) + 4))
+            // shift up unchanged higher entries & empty slots
+            | ((x_ << 4) & ~(((value_type) 256 << (i << 2)) - 1));
+        return value;
+    }
+    /** @brief Insert an unallocated element from position @a si at position @a di.
+        @pre 0 <= @a di < @a width
+        @pre size() < @a width
+        @pre size() <= @a si
+        @return The newly allocated element. */
+    void insert_selected(int di, int si) {
+        (void) width;
+        int value = (*this)[si];
+        value_type mask = ((value_type) 256 << (si << 2)) - 1;
+        // increment size, leave lower slots unchanged
+        x_ = ((x_ + 1) & (((value_type) 16 << (di << 2)) - 1))
+            // insert slot
+            | ((value_type) value << ((di << 2) + 4))
+            // shift up unchanged higher entries & empty slots
+            | ((x_ << 4) & mask & ~(((value_type) 256 << (di << 2)) - 1))
+            // leave uppermost slots alone
+            | (x_ & ~mask);
+    }
+    /** @brief Remove the element at position @a i.
+        @pre 0 <= @a i < @a size()
+        @pre size() < @a width
+
+        Consider the following code:
+        <code>
+        kpermuter<...> p = ..., q = p;
+        q.remove(i);
+        </code>
+
+        The modified permuter, q, has the following properties.
+        <ul>
+        <li>q.size() == p.size() - 1</li>
+        <li>Given j with 0 <= j < i, q[j] == p[j]</li>
+        <li>Given j with i <= j < q.size(), q[j] == p[j+1]</li>
+        <li>q[q.size()] == p[i]</li>
+        </ul> */
+    void remove(int i) {
+        (void) width;
+        if (int(x_ & 15) == i + 1)
+            --x_;
+        else {
+            int rot_amount = ((x_ & 15) - i - 1) << 2;
+            value_type rot_mask =
+                (((value_type) 16 << rot_amount) - 1) << ((i + 1) << 2);
+            // decrement size, leave lower slots unchanged
+            x_ = ((x_ - 1) & ~rot_mask)
+                // shift higher entries down
+                | (((x_ & rot_mask) >> 4) & rot_mask)
+                // shift value up
+                | (((x_ & rot_mask) << rot_amount) & rot_mask);
+        }
+    }
+    /** @brief Remove the element at position @a i to the back.
+        @pre 0 <= @a i < @a size()
+        @pre size() < @a width
+
+        Consider the following code:
+        <code>
+        kpermuter<...> p = ..., q = p;
+        q.remove_to_back(i);
+        </code>
+
+        The modified permuter, q, has the following properties.
+        <ul>
+        <li>q.size() == p.size() - 1</li>
+        <li>Given j with 0 <= j < i, q[j] == p[j]</li>
+        <li>Given j with i <= j < @a width - 1, q[j] == p[j+1]</li>
+        <li>q.back() == p[i]</li>
+        </ul> */
+    void remove_to_back(int i) {
+        value_type mask = ~(((value_type) 16 << (i << 2)) - 1);
+        // clear unused slots
+        value_type x = x_ & (((value_type) 16 << (width << 2)) - 1);
+        // decrement size, leave lower slots unchanged
+        x_ = ((x - 1) & ~mask)
+            // shift higher entries down
+            | ((x >> 4) & mask)
+            // shift removed element up
+            | ((x & mask) << ((width - i - 1) << 2));
+    }
+    /** @brief Rotate the permuter's elements between @a i and size().
+        @pre 0 <= @a i <= @a j <= size()
+
+        Consider the following code:
+        <code>
+        kpermuter<...> p = ..., q = p;
+        q.rotate(i, j);
+        </code>
+
+        The modified permuter, q, has the following properties.
+        <ul>
+        <li>q.size() == p.size()</li>
+        <li>Given k with 0 <= k < i, q[k] == p[k]</li>
+        <li>Given k with i <= k < q.size(), q[k] == p[i + (k - i + j - i) mod (size() - i)]</li>
+        </ul> */
+    void rotate(int i, int j) {
+        value_type mask = (i == width ? (value_type) 0 : (value_type) 16 << (i << 2)) - 1;
+        // clear unused slots
+        value_type x = x_ & (((value_type) 16 << (width << 2)) - 1);
+        x_ = (x & mask)
+            | ((x >> ((j - i) << 2)) & ~mask)
+            | ((x & ~mask) << ((width - j) << 2));
+    }
+    /** @brief Exchange the elements at positions @a i and @a j. */
+    void exchange(int i, int j) {
+        value_type diff = ((x_ >> (i << 2)) ^ (x_ >> (j << 2))) & 240;
+        x_ ^= (diff << (i << 2)) | (diff << (j << 2));
+    }
+    /** @brief Exchange positions of values @a x and @a y. */
+    void exchange_values(int x, int y) {
+        value_type diff = 0, p = x_;
+        for (int i = 0; i < width; ++i, diff <<= 4, p <<= 4) {
+            int v = (p >> (width << 2)) & 15;
+            diff ^= -((v == x) | (v == y)) & (x ^ y);
+        }
+        x_ ^= diff;
+    }
+
+    lcdf::String unparse() const;
+
+    bool operator==(const kpermuter<width>& x) const {
+        return x_ == x.x_;
+    }
+    bool operator!=(const kpermuter<width>& x) const {
+        return !(*this == x);
+    }
+
+    static inline int size(value_type p) {
+        return p & 15;
+    }
+  private:
+    value_type x_;
+};
+
+template <int width>
+lcdf::String kpermuter<width>::unparse() const
+{
+    char buf[max_width + 3], *s = buf;
+    value_type p(x_);
+    value_type seen(0);
+    int n = p & 15;
+    p >>= 4;
+    for (int i = 0; true; ++i) {
+        if (i == n)
+            *s++ = ':';
+        if (i == width)
+            break;
+        if ((p & 15) < 10)
+            *s++ = '0' + (p & 15);
+        else
+            *s++ = 'a' + (p & 15) - 10;
+        seen |= 1 << (p & 15);
+        p >>= 4;
+    }
+    if (seen != (1 << width) - 1) {
+        *s++ = '?';
+        *s++ = '!';
+    }
+    return lcdf::String(buf, s);
+}
+
+
+template <typename T> struct has_permuter_type {
+    template <typename C> static char test(typename C::permuter_type *);
+    template <typename> static int test(...);
+    static constexpr bool value = sizeof(test<T>(0)) == 1;
+};
+
+template <typename T, bool HP = has_permuter_type<T>::value> struct key_permuter {};
+template <typename T> struct key_permuter<T, true> {
+    typedef typename T::permuter_type type;
+    static type permutation(const T& n) {
+        return n.permutation();
+    }
+};
+template <typename T> struct key_permuter<T, false> {
+    typedef identity_kpermuter type;
+    static type permutation(const T& n) {
+        return identity_kpermuter(n.size());
+    }
+};
+
+#endif
diff --git a/silo/masstree/ksearch.hh b/silo/masstree/ksearch.hh
new file mode 100644 (file)
index 0000000..aeb4a28
--- /dev/null
@@ -0,0 +1,173 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KSEARCH_HH
+#define KSEARCH_HH 1
+#include "kpermuter.hh"
+
+template <typename KA, typename T>
+struct key_comparator {
+    int operator()(const KA& ka, const T& n, int p) {
+        return n.compare_key(ka, p);
+    }
+};
+
+struct key_indexed_position {
+    int i;
+    int p;
+    inline key_indexed_position() {
+    }
+    inline constexpr key_indexed_position(int i_, int p_)
+        : i(i_), p(p_) {
+    }
+};
+
+
+template <typename KA, typename T, typename F>
+int key_upper_bound_by(const KA& ka, const T& n, F comparator)
+{
+    typename key_permuter<T>::type perm = key_permuter<T>::permutation(n);
+    int l = 0, r = perm.size();
+    while (l < r) {
+        int m = (l + r) >> 1;
+        int mp = perm[m];
+        int cmp = comparator(ka, n, mp);
+        if (cmp < 0)
+            r = m;
+        else if (cmp == 0)
+            return m + 1;
+        else
+            l = m + 1;
+    }
+    return l;
+}
+
+template <typename KA, typename T>
+inline int key_upper_bound(const KA& ka, const T& n)
+{
+    return key_upper_bound_by(ka, n, key_comparator<KA, T>());
+}
+
+template <typename KA, typename T, typename F>
+key_indexed_position key_lower_bound_by(const KA& ka, const T& n, F comparator)
+{
+    typename key_permuter<T>::type perm = key_permuter<T>::permutation(n);
+    int l = 0, r = perm.size();
+    while (l < r) {
+        int m = (l + r) >> 1;
+        int mp = perm[m];
+        int cmp = comparator(ka, n, mp);
+        if (cmp < 0)
+            r = m;
+        else if (cmp == 0)
+            return key_indexed_position(m, mp);
+        else
+            l = m + 1;
+    }
+    return key_indexed_position(l, -1);
+}
+
+template <typename KA, typename T>
+inline key_indexed_position key_lower_bound(const KA& ka, const T& n)
+{
+    return key_lower_bound_by(ka, n, key_comparator<KA, T>());
+}
+
+
+template <typename KA, typename T, typename F>
+int key_find_upper_bound_by(const KA& ka, const T& n, F comparator)
+{
+    typename key_permuter<T>::type perm = key_permuter<T>::permutation(n);
+    int l = 0, r = perm.size();
+    while (l < r) {
+        int lp = perm[l];
+        int cmp = comparator(ka, n, lp);
+        if (cmp < 0)
+            break;
+        else
+            ++l;
+    }
+    return l;
+}
+
+template <typename KA, typename T, typename F>
+key_indexed_position key_find_lower_bound_by(const KA& ka, const T& n, F comparator)
+{
+    typename key_permuter<T>::type perm = key_permuter<T>::permutation(n);
+    int l = 0, r = perm.size();
+    while (l < r) {
+        int lp = perm[l];
+        int cmp = comparator(ka, n, lp);
+        if (cmp < 0)
+            break;
+        else if (cmp == 0)
+            return key_indexed_position(l, lp);
+        else
+            ++l;
+    }
+    return key_indexed_position(l, -1);
+}
+
+
+struct key_bound_binary {
+    static constexpr bool is_binary = true;
+    template <typename KA, typename T>
+    static inline int upper(const KA& ka, const T& n) {
+        return key_upper_bound_by(ka, n, key_comparator<KA, T>());
+    }
+    template <typename KA, typename T>
+    static inline key_indexed_position lower(const KA& ka, const T& n) {
+        return key_lower_bound_by(ka, n, key_comparator<KA, T>());
+    }
+    template <typename KA, typename T, typename F>
+    static inline key_indexed_position lower_by(const KA& ka, const T& n, F comparator) {
+        return key_lower_bound_by(ka, n, comparator);
+    }
+};
+
+struct key_bound_linear {
+    static constexpr bool is_binary = false;
+    template <typename KA, typename T>
+    static inline int upper(const KA& ka, const T& n) {
+        return key_find_upper_bound_by(ka, n, key_comparator<KA, T>());
+    }
+    template <typename KA, typename T>
+    static inline key_indexed_position lower(const KA& ka, const T& n) {
+        return key_find_lower_bound_by(ka, n, key_comparator<KA, T>());
+    }
+    template <typename KA, typename T, typename F>
+    static inline key_indexed_position lower_by(const KA& ka, const T& n, F comparator) {
+        return key_find_lower_bound_by(ka, n, comparator);
+    }
+};
+
+
+enum {
+    bound_method_fast = 0,
+    bound_method_binary,
+    bound_method_linear
+};
+template <int max_size, int method = bound_method_fast> struct key_bound {};
+template <int max_size> struct key_bound<max_size, bound_method_binary> {
+    typedef key_bound_binary type;
+};
+template <int max_size> struct key_bound<max_size, bound_method_linear> {
+    typedef key_bound_linear type;
+};
+template <int max_size> struct key_bound<max_size, bound_method_fast> {
+    typedef typename key_bound<max_size, (max_size > 16 ? bound_method_binary : bound_method_linear)>::type type;
+};
+
+#endif
diff --git a/silo/masstree/kvio.cc b/silo/masstree/kvio.cc
new file mode 100644 (file)
index 0000000..8a3f5b8
--- /dev/null
@@ -0,0 +1,112 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+// buffered read and write for kvc/kvd.
+// stdio is good but not quite what I want.
+// need to be able to check if any input
+// available, and do non-blocking check.
+// also, fwrite just isn't very fast, at
+// least on the Mac.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include "kvio.hh"
+
+
+// API to allocate a new kvout.
+kvout* new_kvout(int fd, int buflen) {
+    kvout* kv = (kvout*) malloc(sizeof(kvout));
+    assert(kv);
+    memset(kv, 0, sizeof(*kv));
+    kv->capacity = buflen;
+    kv->buf = (char*) malloc(kv->capacity);
+    assert(kv->buf);
+    kv->fd = fd;
+    return kv;
+}
+
+// API to allocate a new kvout for a buffer, no fd.
+kvout* new_bufkvout() {
+    kvout *kv = (kvout*) malloc(sizeof(kvout));
+    assert(kv);
+    memset(kv, 0, sizeof(*kv));
+    kv->capacity = 256;
+    kv->buf = (char*) malloc(kv->capacity);
+    assert(kv->buf);
+    kv->n = 0;
+    kv->fd = -1;
+    return kv;
+}
+
+// API to clear out a buf kvout.
+void kvout_reset(kvout* kv) {
+    assert(kv->fd < 0);
+    kv->n = 0;
+}
+
+// API to free a kvout.
+// does not close() the fd.
+void free_kvout(kvout* kv) {
+    if (kv->buf)
+        free(kv->buf);
+    kv->buf = 0;
+    free(kv);
+}
+
+void kvflush(kvout* kv) {
+    assert(kv->fd >= 0);
+    size_t sent = 0;
+    while (kv->n > sent) {
+        ssize_t cc = write(kv->fd, kv->buf + sent, kv->n - sent);
+        if (cc <= 0) {
+            if (errno == EWOULDBLOCK) {
+                usleep(1);
+                continue;
+            }
+            perror("kvflush write");
+            return;
+        }
+        sent += cc;
+    }
+    kv->n = 0;
+}
+
+// API
+void kvout::grow(unsigned want) {
+    if (fd >= 0)
+        kvflush(this);
+    if (want == 0)
+        want = capacity + 1;
+    while (want > capacity)
+        capacity *= 2;
+    buf = (char*) realloc(buf, capacity);
+    assert(buf);
+}
+
+int kvwrite(kvout* kv, const void* buf, unsigned n) {
+    if (kv->n + n > kv->capacity && kv->fd >= 0)
+        kvflush(kv);
+    if (kv->n + n > kv->capacity)
+        kv->grow(kv->n + n);
+    memcpy(kv->buf + kv->n, buf, n);
+    kv->n += n;
+    return n;
+}
diff --git a/silo/masstree/kvio.hh b/silo/masstree/kvio.hh
new file mode 100644 (file)
index 0000000..5aea020
--- /dev/null
@@ -0,0 +1,67 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVIO_H
+#define KVIO_H
+#include <string>
+#include <vector>
+#include <stdlib.h>
+#include "string.hh"
+#include "str.hh"
+
+struct kvout {
+    int fd;
+    char* buf;
+    unsigned capacity; // allocated size of buf
+    unsigned n;   // # of chars we've written to buf
+
+    inline void append(char c);
+    inline char* reserve(int n);
+    inline void adjust_length(int delta);
+    inline void set_end(char* end);
+    void grow(unsigned want);
+};
+
+kvout* new_kvout(int fd, int buflen);
+kvout* new_bufkvout();
+void kvout_reset(kvout* kv);
+void free_kvout(kvout* kv);
+int kvwrite(kvout* kv, const void* buf, unsigned int n);
+void kvflush(kvout* kv);
+
+inline void kvout::append(char c) {
+    if (n == capacity)
+        grow(0);
+    buf[n] = c;
+    ++n;
+}
+
+inline char* kvout::reserve(int nchars) {
+    if (n + nchars > capacity)
+        grow(n + nchars);
+    return buf + n;
+}
+
+inline void kvout::adjust_length(int delta) {
+    masstree_precondition(n + delta <= capacity);
+    n += delta;
+}
+
+inline void kvout::set_end(char* x) {
+    masstree_precondition(x >= buf && x <= buf + capacity);
+    n = x - buf;
+}
+
+#endif
diff --git a/silo/masstree/kvproto.hh b/silo/masstree/kvproto.hh
new file mode 100644 (file)
index 0000000..d14b207
--- /dev/null
@@ -0,0 +1,57 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVPROTO_HH
+#define KVPROTO_HH
+#include "compiler.hh"
+
+enum {
+    Cmd_None = 0,
+    Cmd_Get = 2,
+    Cmd_Scan = 4,
+    Cmd_Put = 6,
+    Cmd_Replace = 8,
+    Cmd_Remove = 10,
+    Cmd_Checkpoint = 12,
+    Cmd_Handshake = 14,
+    Cmd_Max
+};
+
+enum result_t {
+    NotFound = -2,
+    Retry,
+    OutOfDate,
+    Inserted,
+    Updated,
+    Found,
+    ScanDone
+};
+
+enum ckptrav_order_t {
+    ckptrav_inorder = 0,
+    ckptrav_preorder
+};
+
+struct row_marker {
+    enum { mt_remove = 1, mt_delta = 2 };
+    int marker_type_;
+};
+
+template <typename R>
+inline bool row_is_marker(const R* row) {
+    return row->timestamp() & 1;
+}
+
+#endif
diff --git a/silo/masstree/kvrandom.cc b/silo/masstree/kvrandom.cc
new file mode 100644 (file)
index 0000000..a70a785
--- /dev/null
@@ -0,0 +1,38 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "kvrandom.hh"
+#include "compiler.hh"
+#include <stdio.h>
+
+const uint32_t kvrandom_psdes_nr::c1[] = {
+    0xBAA96887U, 0x1E17D32CU, 0x03BCDC3CU, 0x0F33D1B2U
+};
+const uint32_t kvrandom_psdes_nr::c2[] = {
+    0x4B0F3B58U, 0xE874F0C3U, 0x6955C5A6U, 0x55A7CA46U
+};
+
+uint32_t kvrandom_psdes_nr::psdes(uint32_t lword, uint32_t irword) {
+    for (int i = 0; i < niter; ++i) {
+       uint32_t iswap = irword;
+       uint32_t ia = irword ^ c1[i];
+       uint32_t il = ia & 0xFFFF, ih = ia >> 16;
+       uint32_t ib = il * il + ~(ih * ih);
+       ia = (ib >> 16) | (ib << 16);
+       irword = lword ^ ((ia ^ c2[i]) + il * ih);
+       lword = iswap;
+    }
+    return irword;
+}
diff --git a/silo/masstree/kvrandom.hh b/silo/masstree/kvrandom.hh
new file mode 100644 (file)
index 0000000..5331e01
--- /dev/null
@@ -0,0 +1,99 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVRANDOM_HH
+#define KVRANDOM_HH 1
+#include <inttypes.h>
+#include <stdlib.h>
+
+// A simple LCG with parameters from Numerical Recipes.
+class kvrandom_lcg_nr_simple { public:
+    enum { min_value = 0, max_value = 0xFFFFFFFFU };
+    typedef uint32_t value_type;
+    typedef uint32_t seed_type;
+    kvrandom_lcg_nr_simple()
+       : seed_(default_seed) {
+    }
+    explicit kvrandom_lcg_nr_simple(seed_type seed)
+       : seed_(seed) {
+    }
+    void reset(seed_type seed) {
+       seed_ = seed;
+    }
+    value_type next() {
+       return (seed_ = seed_ * a + c);
+    }
+  private:
+    uint32_t seed_;
+    enum { default_seed = 819234718U, a = 1664525U, c = 1013904223U };
+};
+
+// A combination version of the NR LCG that uses only its higher order
+// digits. (In the default NR LCG the lowest bits have less randomness; e.g.,
+// the low bit flips between 0 and 1 with every call.)
+class kvrandom_lcg_nr : public kvrandom_lcg_nr_simple { public:
+    enum { min_value = 0, max_value = 0x7FFFFFFF };
+    typedef int32_t value_type;
+    value_type next() {
+       uint32_t x0 = kvrandom_lcg_nr_simple::next(),
+           x1 = kvrandom_lcg_nr_simple::next();
+       return (x0 >> 15) | ((x1 & 0x7FFE) << 16);
+    }
+};
+
+// A random number generator taken from NR's ran4. Based on hashing.
+class kvrandom_psdes_nr { public:
+    enum { min_value = 0, max_value = 0xFFFFFFFFU };
+    typedef uint32_t value_type;
+    typedef uint32_t seed_type;
+    kvrandom_psdes_nr() {
+       reset(1);
+    }
+    explicit kvrandom_psdes_nr(seed_type seed) {
+       reset(seed);
+    }
+    void reset(seed_type seed) {
+       seed_ = seed;
+       next_ = 1;
+    }
+    value_type next() {
+       uint32_t value = psdes(seed_, next_);
+       ++next_;
+       return value;
+    }
+    value_type operator[](uint32_t index) const {
+       return psdes(seed_, index);
+    }
+  private:
+    uint32_t seed_;
+    uint32_t next_;
+    enum { niter = 4 };
+    static const uint32_t c1[niter], c2[niter];
+    static uint32_t psdes(uint32_t lword, uint32_t irword);
+};
+
+// a wrapper around random(), for backwards compatibility
+class kvrandom_random { public:
+    kvrandom_random() {
+    }
+    void reset(uint32_t seed) {
+       srandom(seed);
+    }
+    int32_t next() const {
+       return random();
+    }
+};
+
+#endif
diff --git a/silo/masstree/kvrow.hh b/silo/masstree/kvrow.hh
new file mode 100644 (file)
index 0000000..81e71d8
--- /dev/null
@@ -0,0 +1,321 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVROW_HH
+#define KVROW_HH 1
+#include "kvthread.hh"
+#include "kvproto.hh"
+#include "log.hh"
+#include "json.hh"
+#include <algorithm>
+
+#if MASSTREE_ROW_TYPE_ARRAY
+# include "value_array.hh"
+typedef value_array row_type;
+#elif MASSTREE_ROW_TYPE_ARRAY_VER
+# include "value_versioned_array.hh"
+typedef value_versioned_array row_type;
+#elif MASSTREE_ROW_TYPE_STR
+# include "value_string.hh"
+typedef value_string row_type;
+#else
+# include "value_bag.hh"
+typedef value_bag<uint16_t> row_type;
+#endif
+
+template <typename R>
+struct query_helper {
+    inline const R* snapshot(const R* row, const std::vector<typename R::index_type>&, threadinfo&) {
+        return row;
+    }
+};
+
+template <typename R> class query_json_scanner;
+
+template <typename R>
+class query {
+  public:
+    typedef lcdf::Json Json;
+
+    template <typename T>
+    void run_get(T& table, Json& req, threadinfo& ti);
+    template <typename T>
+    bool run_get1(T& table, Str key, int col, Str& value, threadinfo& ti);
+
+    template <typename T>
+    result_t run_put(T& table, Str key,
+                     const Json* firstreq, const Json* lastreq, threadinfo& ti);
+    template <typename T>
+    result_t run_replace(T& table, Str key, Str value, threadinfo& ti);
+    template <typename T>
+    bool run_remove(T& table, Str key, threadinfo& ti);
+
+    template <typename T>
+    void run_scan(T& table, Json& request, threadinfo& ti);
+    template <typename T>
+    void run_rscan(T& table, Json& request, threadinfo& ti);
+
+    const loginfo::query_times& query_times() const {
+        return qtimes_;
+    }
+
+  private:
+    std::vector<typename R::index_type> f_;
+    loginfo::query_times qtimes_;
+    query_helper<R> helper_;
+    lcdf::String scankey_;
+    int scankeypos_;
+
+    void emit_fields(const R* value, Json& req, threadinfo& ti);
+    void emit_fields1(const R* value, Json& req, threadinfo& ti);
+    void assign_timestamp(threadinfo& ti);
+    void assign_timestamp(threadinfo& ti, kvtimestamp_t t);
+    inline bool apply_put(R*& value, bool found, const Json* firstreq,
+                          const Json* lastreq, threadinfo& ti);
+    inline bool apply_replace(R*& value, bool found, Str new_value,
+                              threadinfo& ti);
+    inline void apply_remove(R*& value, kvtimestamp_t& node_ts, threadinfo& ti);
+
+    template <typename RR> friend class query_json_scanner;
+};
+
+
+template <typename R>
+void query<R>::emit_fields(const R* value, Json& req, threadinfo& ti) {
+    const R* snapshot = helper_.snapshot(value, f_, ti);
+    if (f_.empty()) {
+        for (int i = 0; i != snapshot->ncol(); ++i)
+            req.push_back(lcdf::String::make_stable(snapshot->col(i)));
+    } else {
+        for (int i = 0; i != (int) f_.size(); ++i)
+            req.push_back(lcdf::String::make_stable(snapshot->col(f_[i])));
+    }
+}
+
+template <typename R>
+void query<R>::emit_fields1(const R* value, Json& req, threadinfo& ti) {
+    const R* snapshot = helper_.snapshot(value, f_, ti);
+    if ((f_.empty() && snapshot->ncol() == 1) || f_.size() == 1)
+        req = lcdf::String::make_stable(snapshot->col(f_.empty() ? 0 : f_[0]));
+    else if (f_.empty()) {
+        for (int i = 0; i != snapshot->ncol(); ++i)
+            req.push_back(lcdf::String::make_stable(snapshot->col(i)));
+    } else {
+        for (int i = 0; i != (int) f_.size(); ++i)
+            req.push_back(lcdf::String::make_stable(snapshot->col(f_[i])));
+    }
+}
+
+
+template <typename R> template <typename T>
+void query<R>::run_get(T& table, Json& req, threadinfo& ti) {
+    typename T::unlocked_cursor_type lp(table, req[2].as_s());
+    bool found = lp.find_unlocked(ti);
+    if (found && row_is_marker(lp.value()))
+        found = false;
+    if (found) {
+        f_.clear();
+        for (int i = 3; i != req.size(); ++i)
+            f_.push_back(req[i].as_i());
+        req.resize(2);
+        emit_fields(lp.value(), req, ti);
+    }
+}
+
+template <typename R> template <typename T>
+bool query<R>::run_get1(T& table, Str key, int col, Str& value, threadinfo& ti) {
+    typename T::unlocked_cursor_type lp(table, key);
+    bool found = lp.find_unlocked(ti);
+    if (found && row_is_marker(lp.value()))
+        found = false;
+    if (found)
+        value = lp.value()->col(col);
+    return found;
+}
+
+
+template <typename R>
+inline void query<R>::assign_timestamp(threadinfo& ti) {
+    qtimes_.ts = ti.update_timestamp();
+    qtimes_.prev_ts = 0;
+}
+
+template <typename R>
+inline void query<R>::assign_timestamp(threadinfo& ti, kvtimestamp_t min_ts) {
+    qtimes_.ts = ti.update_timestamp(min_ts);
+    qtimes_.prev_ts = min_ts;
+}
+
+
+template <typename R> template <typename T>
+result_t query<R>::run_put(T& table, Str key,
+                           const Json* firstreq, const Json* lastreq,
+                           threadinfo& ti) {
+    typename T::cursor_type lp(table, key);
+    bool found = lp.find_insert(ti);
+    if (!found)
+        ti.advance_timestamp(lp.node_timestamp());
+    bool inserted = apply_put(lp.value(), found, firstreq, lastreq, ti);
+    lp.finish(1, ti);
+    return inserted ? Inserted : Updated;
+}
+
+template <typename R>
+inline bool query<R>::apply_put(R*& value, bool found, const Json* firstreq,
+                                const Json* lastreq, threadinfo& ti) {
+    if (loginfo* log = ti.logger()) {
+       log->acquire();
+       qtimes_.epoch = global_log_epoch;
+    }
+
+    if (!found) {
+    insert:
+       assign_timestamp(ti);
+        value = R::create(firstreq, lastreq, qtimes_.ts, ti);
+        return true;
+    }
+
+    R* old_value = value;
+    assign_timestamp(ti, old_value->timestamp());
+    if (row_is_marker(old_value)) {
+       old_value->deallocate_rcu(ti);
+       goto insert;
+    }
+
+    R* updated = old_value->update(firstreq, lastreq, qtimes_.ts, ti);
+    if (updated != old_value) {
+       value = updated;
+       old_value->deallocate_rcu_after_update(firstreq, lastreq, ti);
+    }
+    return false;
+}
+
+template <typename R> template <typename T>
+result_t query<R>::run_replace(T& table, Str key, Str value, threadinfo& ti) {
+    typename T::cursor_type lp(table, key);
+    bool found = lp.find_insert(ti);
+    if (!found)
+        ti.advance_timestamp(lp.node_timestamp());
+    bool inserted = apply_replace(lp.value(), found, value, ti);
+    lp.finish(1, ti);
+    return inserted ? Inserted : Updated;
+}
+
+template <typename R>
+inline bool query<R>::apply_replace(R*& value, bool found, Str new_value,
+                                    threadinfo& ti) {
+    if (loginfo* log = ti.logger()) {
+       log->acquire();
+       qtimes_.epoch = global_log_epoch;
+    }
+
+    bool inserted = !found || row_is_marker(value);
+    if (!found)
+       assign_timestamp(ti);
+    else {
+        assign_timestamp(ti, value->timestamp());
+        value->deallocate_rcu(ti);
+    }
+
+    value = R::create1(new_value, qtimes_.ts, ti);
+    return inserted;
+}
+
+template <typename R> template <typename T>
+bool query<R>::run_remove(T& table, Str key, threadinfo& ti) {
+    typename T::cursor_type lp(table, key);
+    bool found = lp.find_locked(ti);
+    if (found)
+        apply_remove(lp.value(), lp.node_timestamp(), ti);
+    lp.finish(-1, ti);
+    return found;
+}
+
+template <typename R>
+inline void query<R>::apply_remove(R*& value, kvtimestamp_t& node_ts,
+                                   threadinfo& ti) {
+    if (loginfo* log = ti.logger()) {
+       log->acquire();
+       qtimes_.epoch = global_log_epoch;
+    }
+
+    R* old_value = value;
+    assign_timestamp(ti, old_value->timestamp());
+    if (circular_int<kvtimestamp_t>::less_equal(node_ts, qtimes_.ts))
+       node_ts = qtimes_.ts + 2;
+    old_value->deallocate_rcu(ti);
+}
+
+
+template <typename R>
+class query_json_scanner {
+  public:
+    query_json_scanner(query<R> &q, lcdf::Json& request)
+       : q_(q), nleft_(request[3].as_i()), request_(request) {
+        std::swap(request[2].value().as_s(), firstkey_);
+        request_.resize(2);
+        q_.scankeypos_ = 0;
+    }
+    const lcdf::String& firstkey() const {
+        return firstkey_;
+    }
+    template <typename SS, typename K>
+    void visit_leaf(const SS&, const K&, threadinfo&) {
+    }
+    bool visit_value(Str key, R* value, threadinfo& ti) {
+        if (row_is_marker(value))
+            return true;
+        // NB the `key` is not stable! We must save space for it.
+        while (q_.scankeypos_ + key.length() > q_.scankey_.length()) {
+            q_.scankey_ = lcdf::String::make_uninitialized(q_.scankey_.length() ? q_.scankey_.length() * 2 : 1024);
+            q_.scankeypos_ = 0;
+        }
+        memcpy(const_cast<char*>(q_.scankey_.data() + q_.scankeypos_),
+               key.data(), key.length());
+        request_.push_back(q_.scankey_.substr(q_.scankeypos_, key.length()));
+        q_.scankeypos_ += key.length();
+        request_.push_back(lcdf::Json());
+        q_.emit_fields1(value, request_.back(), ti);
+        --nleft_;
+        return nleft_ != 0;
+    }
+  private:
+    query<R> &q_;
+    int nleft_;
+    lcdf::Json& request_;
+    lcdf::String firstkey_;
+};
+
+template <typename R> template <typename T>
+void query<R>::run_scan(T& table, Json& request, threadinfo& ti) {
+    assert(request[3].as_i() > 0);
+    f_.clear();
+    for (int i = 4; i != request.size(); ++i)
+        f_.push_back(request[i].as_i());
+    query_json_scanner<R> scanf(*this, request);
+    table.scan(scanf.firstkey(), true, scanf, ti);
+}
+
+template <typename R> template <typename T>
+void query<R>::run_rscan(T& table, Json& request, threadinfo& ti) {
+    assert(request[3].as_i() > 0);
+    f_.clear();
+    for (int i = 4; i != request.size(); ++i)
+        f_.push_back(request[i].as_i());
+    query_json_scanner<R> scanf(*this, request);
+    table.rscan(scanf.firstkey(), true, scanf, ti);
+}
+
+#endif
diff --git a/silo/masstree/kvstats.hh b/silo/masstree/kvstats.hh
new file mode 100644 (file)
index 0000000..c4775e3
--- /dev/null
@@ -0,0 +1,53 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVSTATS_HH
+#define KVSTATS_HH 1
+#include <stdlib.h>
+
+struct kvstats {
+  double min, max, sum, sumsq;
+  long count;
+  kvstats()
+    : min(-1), max(-1), sum(0), sumsq(0), count(0) {
+  }
+  void add(double x) {
+    if (!count || x < min)
+      min = x;
+    if (max < x)
+      max = x;
+    sum += x;
+    sumsq += x * x;
+    count += 1;
+  }
+  typedef void (kvstats::*unspecified_bool_type)(double);
+  operator unspecified_bool_type() const {
+    return count ? &kvstats::add : 0;
+  }
+  void print_report(const char *name) const {
+    if (count)
+      printf("%s: n %ld, total %.0f, average %.0f, min %.0f, max %.0f, stddev %.0f\n",
+            name, count, sum, sum / count, min, max,
+            sqrt((sumsq - sum * sum / count) / (count - 1)));
+  }
+  double avg() {
+    if (count)
+      return sum / count;
+    else
+      return 0;
+  }
+};
+
+#endif
diff --git a/silo/masstree/kvtest.hh b/silo/masstree/kvtest.hh
new file mode 100644 (file)
index 0000000..a9645b2
--- /dev/null
@@ -0,0 +1,1627 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVTEST_HH
+#define KVTEST_HH
+#include "json.hh"
+#include "misc.hh"
+#include "kvproto.hh"
+#include <vector>
+#include <fstream>
+
+using lcdf::Str;
+using lcdf::String;
+using lcdf::Json;
+extern int kvtest_first_seed;
+// Templated KV tests, so we can run them either client/server or linked with
+// the kvd binary.
+
+template <typename N>
+inline Json& kvtest_set_time(Json& result, const lcdf::String& base, N n, double delta_t)
+{
+    result.set(base, n);
+    if (delta_t > 0)
+        result.set(base + "_per_sec", n / delta_t);
+    return result;
+}
+
+template <typename N>
+inline Json kvtest_set_time(const Json& result, const lcdf::String& base, N n, double delta_t) {
+    Json x(result);
+    kvtest_set_time(x, base, n, delta_t);
+    return x;
+}
+
+template <typename C>
+void kvtest_sync_rw1_seed(C &client, int seed)
+{
+    client.rand.reset(seed);
+    double tp0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
+        int32_t x = (int32_t) client.rand.next();
+        client.put_sync(x, x + 1);
+    }
+    client.wait_all();
+    double tp1 = client.now();
+
+    client.puts_done();
+    client.notice("now getting\n");
+    int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
+    assert(a);
+    client.rand.reset(seed);
+    for (unsigned i = 0; i < n; ++i)
+        a[i] = (int32_t) client.rand.next();
+    for (unsigned i = 0; i < n; ++i)
+        std::swap(a[i], a[client.rand.next() % n]);
+
+    double tg0 = client.now();
+    unsigned g;
+    for (g = 0; g < n && !client.timeout(1); ++g)
+        client.get_check_sync(a[g], a[g] + 1);
+    client.wait_all();
+    double tg1 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, tp1 - tp0);
+    kvtest_set_time(result, "gets", g, tg1 - tg0);
+    kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
+    client.report(result);
+    free(a);
+}
+
+template <typename C>
+void kvtest_sync_rw1(C &client)
+{
+    kvtest_sync_rw1_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+template <typename C>
+unsigned kvtest_rw1puts_seed(C& client, int seed) {
+    client.rand.reset(seed);
+    double tp0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
+        int32_t x = (int32_t) client.rand.next();
+        client.put(x, x + 1);
+    }
+    client.wait_all();
+    double tp1 = client.now();
+    client.puts_done();
+
+    client.report(kvtest_set_time(Json(), "puts", n, tp1 - tp0));
+    return n;
+}
+
+// do a bunch of inserts to distinct keys, then check that they all showed up.
+// sometimes overwrites, but only w/ same value.
+// different clients might use same key sometimes.
+template <typename C>
+void kvtest_rw1_seed(C &client, int seed)
+{
+    unsigned n = kvtest_rw1puts_seed(client, seed);
+
+    client.notice("now getting\n");
+    int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
+    assert(a);
+    client.rand.reset(seed);
+    for (unsigned i = 0; i < n; ++i)
+        a[i] = (int32_t) client.rand.next();
+    for (unsigned i = 0; i < n; ++i)
+        std::swap(a[i], a[client.rand.next() % n]);
+
+    double tg0 = client.now();
+    unsigned g;
+#if 0
+#define BATCH 8
+    for(g = 0; g+BATCH < n && !client.timeout(1); g += BATCH){
+      long key[BATCH], expected[BATCH];
+      for(int i = 0; i < BATCH; i++){
+        key[i] = a[g+i];
+        expected[i] = a[g+i] + 1;
+      }
+      client.many_get_check(BATCH, key, expected);
+    }
+#else
+    for (g = 0; g < n && !client.timeout(1); ++g)
+        client.get_check(a[g], a[g] + 1);
+#endif
+    client.wait_all();
+    double tg1 = client.now();
+
+    Json result = client.report(Json());
+    kvtest_set_time(result, "gets", g, tg1 - tg0);
+    double delta_puts = n / result["puts_per_sec"].as_d();
+    kvtest_set_time(result, "ops", n + g, delta_puts + (tg1 - tg0));
+    client.report(result);
+    free(a);
+}
+
+template <typename C>
+void kvtest_rw1puts(C &client)
+{
+    kvtest_rw1puts_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+template <typename C>
+void kvtest_rw1(C &client)
+{
+    kvtest_rw1_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+// do a bunch of inserts to distinct keys, then check that they all showed up.
+// sometimes overwrites, but only w/ same value.
+// different clients might use same key sometimes.
+template <typename C>
+void kvtest_rw1long_seed(C &client, int seed)
+{
+    const char * const formats[] = {
+        "user%u", "machine%u", "opening%u", "fartparade%u"
+    };
+    char buf[64];
+
+    client.rand.reset(seed);
+    double tp0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
+        unsigned fmt = client.rand.next();
+        int32_t x = (int32_t) client.rand.next();
+        client.put(Str::snprintf(buf, sizeof(buf), formats[fmt % 4], x), x + 1);
+    }
+    client.wait_all();
+    double tp1 = client.now();
+
+    client.puts_done();
+    client.notice("now getting\n");
+    int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n * 2);
+    assert(a);
+    client.rand.reset(seed);
+    for (unsigned i = 0; i < n * 2; ++i)
+        a[i] = (int32_t) client.rand.next();
+    for (unsigned i = 0; i < n; ++i) {
+        unsigned x = client.rand.next() % n;
+        std::swap(a[2 * i], a[2 * x]);
+        std::swap(a[2 * i + 1], a[2 * x + 1]);
+    }
+
+    double tg0 = client.now();
+    unsigned g;
+    for (g = 0; g < n && !client.timeout(1); ++g) {
+        unsigned fmt = a[2 * g];
+        int32_t x = (int32_t) a[2 * g + 1];
+        client.get_check(Str::snprintf(buf, sizeof(buf), formats[fmt % 4], x), x + 1);
+    }
+    client.wait_all();
+    double tg1 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, tp1 - tp0);
+    kvtest_set_time(result, "gets", g, tg1 - tg0);
+    kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
+    client.report(result);
+    free(a);
+}
+
+template <typename C>
+void kvtest_rw1long(C &client)
+{
+    kvtest_rw1long_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+// interleave inserts and gets for random keys.
+template <typename C>
+void kvtest_rw2_seed(C &client, int seed, double getfrac)
+{
+    client.rand.reset(seed);
+    const unsigned c = 2654435761U;
+    const unsigned offset = client.rand.next();
+
+    double t0 = client.now();
+    uint64_t puts = 0, gets = 0;
+    int getfrac65536 = (int) (getfrac * 65536 + 0.5);
+    while (!client.timeout(0) && (puts + gets) <= client.limit()) {
+        if (puts == 0 || (client.rand.next() % 65536) >= getfrac65536) {
+            // insert
+            unsigned x = (offset + puts) * c;
+            client.put(x, x + 1);
+            ++puts;
+        } else {
+            // get
+            unsigned x = (offset + (client.rand.next() % puts)) * c;
+            client.get_check(x, x + 1);
+            ++gets;
+        }
+    }
+    client.wait_all();
+    double t1 = client.now();
+
+    Json result = Json().set("puts", puts).set("gets", gets);
+    kvtest_set_time(result, "ops", puts + gets, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_rw2(C &client)
+{
+    kvtest_rw2_seed(client, kvtest_first_seed + client.id() % 48, 0.5);
+}
+
+template <typename C>
+void kvtest_rw2g90(C &client)
+{
+    kvtest_rw2_seed(client, kvtest_first_seed + client.id() % 48, 0.9);
+}
+
+template <typename C>
+void kvtest_rw2g98(C &client)
+{
+    kvtest_rw2_seed(client, kvtest_first_seed + client.id() % 48, 0.98);
+}
+
+// interleave inserts and gets for random keys.
+template <typename C>
+void kvtest_rw2fixed_seed(C &client, int seed, double getfrac)
+{
+    client.rand.reset(seed);
+    const unsigned c = 2654435761U;
+    const unsigned offset = client.rand.next();
+
+    double t0 = client.now();
+    uint64_t puts = 0, gets = 0;
+    int getfrac65536 = (int) (getfrac * 65536 + 0.5);
+    while (!client.timeout(0) && (puts + gets) <= client.limit()) {
+        if (puts == 0 || (client.rand.next() % 65536) >= getfrac65536) {
+            // insert
+            unsigned x = (offset + puts) * c;
+            x %= 100000000;
+            client.put(x, x + 1);
+            ++puts;
+        } else {
+            // get
+            unsigned x = (offset + (client.rand.next() % puts)) * c;
+            x %= 100000000;
+            client.get_check(x, x + 1);
+            ++gets;
+        }
+    }
+    client.wait_all();
+    double t1 = client.now();
+
+    Json result = Json().set("puts", puts).set("gets", gets);
+    kvtest_set_time(result, "ops", puts + gets, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_rw2fixed(C &client)
+{
+    kvtest_rw2fixed_seed(client, kvtest_first_seed + client.id() % 48, 0.5);
+}
+
+template <typename C>
+void kvtest_rw2fixedg90(C &client)
+{
+    kvtest_rw2fixed_seed(client, kvtest_first_seed + client.id() % 48, 0.9);
+}
+
+template <typename C>
+void kvtest_rw2fixedg98(C &client)
+{
+    kvtest_rw2fixed_seed(client, kvtest_first_seed + client.id() % 48, 0.98);
+}
+
+// do a bunch of inserts to sequentially increasing keys,
+// then check that they all showed up.
+// different clients might use same key sometimes.
+template <typename C>
+void kvtest_rw3(C &client)
+{
+    double t0 = client.now();
+    uint64_t n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n)
+        client.put_key8(n, n + 1);
+    client.wait_all();
+
+    client.puts_done();
+    client.notice("now getting\n");
+
+    double t1 = client.now();
+    for (unsigned i = 0; i < n; ++i)
+        client.get_check_key8(i, i + 1);
+    client.wait_all();
+
+    double t2 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, t1 - t0);
+    kvtest_set_time(result, "gets", n, t2 - t1);
+    kvtest_set_time(result, "ops", n + n, t2 - t0);
+    client.report(result);
+}
+
+// do a bunch of inserts to sequentially decreasing keys,
+// then check that they all showed up.
+// different clients might use same key sometimes.
+template <typename C>
+void kvtest_rw4(C &client)
+{
+    const int top = 2147483647;
+
+    double t0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n)
+        client.put_key8(top - n, n + 1);
+    client.wait_all();
+
+    client.puts_done();
+    client.notice("now getting\n");
+
+    double t1 = client.now();
+    for (unsigned i = 0; i < n; ++i)
+        client.get_check_key8(top - i, i + 1);
+    client.wait_all();
+
+    double t2 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, t1 - t0);
+    kvtest_set_time(result, "gets", n, t2 - t1);
+    kvtest_set_time(result, "ops", n + n, t2 - t0);
+    client.report(result);
+}
+
+// do a bunch of inserts to sequentially decreasing 8B keys,
+// then check that they all showed up.
+// different clients might use same key sometimes.
+template <typename C>
+void kvtest_rw4fixed(C &client)
+{
+    const int top = 99999999;
+
+    double t0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n)
+        client.put_key8(top - n, n + 1);
+    client.wait_all();
+
+    client.puts_done();
+    client.notice("now getting\n");
+
+    double t1 = client.now();
+    for (unsigned i = 0; i < n; ++i)
+        client.get_check_key8(top - i, i + 1);
+    client.wait_all();
+
+    double t2 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, t1 - t0);
+    kvtest_set_time(result, "gets", n, t2 - t1);
+    kvtest_set_time(result, "ops", n + n, t2 - t0);
+    client.report(result);
+}
+
+// update the same small set of keys over and over,
+// to uncover concurrent update bugs in the server.
+template <typename C>
+void kvtest_same_seed(C &client, int seed)
+{
+    client.rand.reset(seed);
+
+    double t0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
+        unsigned x = client.rand.next() % 10;
+        client.put(x, x + 1);
+    }
+    client.wait_all();
+    double t1 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_same(C &client)
+{
+    kvtest_same_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+// update the same small set of keys over and over, with interspersed gets.
+template <typename C>
+void kvtest_rwsmall_seed(C &client, int nkeys, int seed)
+{
+    client.rand.reset(seed);
+
+    double t0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
+        unsigned x = client.rand.next() % (8 * nkeys);
+        if (x & 7)
+            client.get(x >> 3);
+        else
+            client.put(x >> 3, n);
+    }
+    client.wait_all();
+    double t1 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "ops", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_rwsmall24(C &client)
+{
+    kvtest_rwsmall_seed(client, 24, kvtest_first_seed + client.id() % 48);
+}
+
+// update the same small set of keys over and over, with interspersed gets.
+// but ensure that the keys are all on different cache lines.
+template <typename C>
+void kvtest_rwsep_seed(C &client, int nkeys, int clientid, int seed)
+{
+    for (int n = clientid * (32 + nkeys); n < (clientid + 1) * (32 + nkeys); ++n)
+        client.put(1000000 + n, n);
+
+    client.rand.reset(seed);
+
+    double t0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
+        unsigned x = client.rand.next() % (8 * nkeys);
+        if (x & 7)
+            client.get(1000000 + clientid * (32 + nkeys) + (x >> 3));
+        else
+            client.put(1000000 + clientid * (32 + nkeys) + (x >> 3), n);
+    }
+    client.wait_all();
+    double t1 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "ops", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_rwsep24(C &client)
+{
+    kvtest_rwsep_seed(client, 24, client.id(), kvtest_first_seed + client.id() % 48);
+}
+
+// Same as rw1, except that the keys are no more than 8 bytes
+template <typename C>
+void kvtest_rw1fixed_seed(C &client, int seed)
+{
+    client.rand.reset(seed);
+    double tp0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
+        int32_t x = (int32_t) client.rand.next();
+        x %= 100000000;
+        client.put(x, x + 1);
+    }
+    client.wait_all();
+    double tp1 = client.now();
+
+    client.puts_done();
+    client.notice("now getting\n");
+    int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
+    assert(a);
+    client.rand.reset(seed);
+    for (unsigned i = 0; i < n; ++i) {
+        a[i] = (int32_t) client.rand.next();
+        a[i] %= 100000000;
+    }
+    for (unsigned i = 0; i < n; ++i)
+        std::swap(a[i], a[client.rand.next() % n]);
+
+    double tg0 = client.now();
+    unsigned g;
+#if 0
+#define BATCH 8
+    for(g = 0; g+BATCH < n && !client.timeout(1); g += BATCH){
+      long key[BATCH], expected[BATCH];
+      for(int i = 0; i < BATCH; i++){
+        key[i] = a[g+i];
+        expected[i] = a[g+i] + 1;
+      }
+      client.many_get_check(BATCH, key, expected);
+    }
+#else
+    for (g = 0; g < n && !client.timeout(1); ++g)
+        client.get_check(a[g], a[g] + 1);
+#endif
+    client.wait_all();
+    double tg1 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, tp1 - tp0);
+    kvtest_set_time(result, "gets", g, tg1 - tg0);
+    kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
+    client.report(result);
+    free(a);
+}
+
+template <typename C>
+void kvtest_rw1fixed(C &client)
+{
+    kvtest_rw1fixed_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+// Same as rw1, except that keys are 16-bytes (prefixed with "0"s)
+template <typename C>
+void kvtest_rw16_seed(C &client, int seed)
+{
+    client.rand.reset(seed);
+    double tp0 = client.now();
+    int n;
+    char key[256];
+    char val[256];
+    for (n = 0; !client.timeout(0); ++n) {
+        int32_t x = (int32_t) client.rand.next();
+        sprintf(key, "%016d", x);
+        sprintf(val, "%016d", x + 1);
+        client.put(key, val);
+    }
+    client.wait_all();
+    double tp1 = client.now();
+
+    client.puts_done();
+    client.notice("now getting\n");
+    int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
+    assert(a);
+    client.rand.reset(seed);
+    for (int i = 0; i < n; ++i)
+        a[i] = (int32_t) client.rand.next();
+    for (int i = 0; i < n; ++i)
+        std::swap(a[i], a[client.rand.next() % n]);
+
+    double tg0 = client.now();
+    int g;
+    for (g = 0; g < n && !client.timeout(1); ++g) {
+        sprintf(key, "%016d", a[g]);
+        sprintf(val, "%016d", a[g] + 1);
+        client.get_check(key, val);
+    }
+    client.wait_all();
+    double tg1 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, tp1 - tp0);
+    kvtest_set_time(result, "gets", g, tg1 - tg0);
+    kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
+    client.report(result);
+    free(a);
+}
+
+template <typename C>
+void kvtest_rw16(C &client)
+{
+    kvtest_rw16_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+
+// A writer and a deleter; the deleter chases the writer
+template <typename C>
+void kvtest_wd1(unsigned initial_pos, int incr, C &client)
+{
+    incr = std::max(incr, client.nthreads() / 2);
+    unsigned pos = initial_pos + ((client.id() / 2) % incr);
+    unsigned n = 0;
+    Json result = Json();
+
+    double t0 = client.now();
+    if (client.id() % 2) {
+        while (!client.get_sync(pos + 16 * incr))
+            /* spin */;
+        while (!client.timeout(0)) {
+            ++n;
+            if (client.remove_sync(pos))
+                pos += incr;
+            if ((n % (1 << 16)) == 0)
+                client.rcu_quiesce();
+        }
+        result.set("removepos", pos);
+    } else {
+        while (!client.timeout(0)) {
+            ++n;
+            client.put(pos, pos + 1);
+            pos += incr;
+            if ((n % (1 << 16)) == 0)
+                client.rcu_quiesce();
+        }
+        result.set("putpos", pos);
+    }
+    client.wait_all();
+    double t1 = client.now();
+
+    kvtest_set_time(result, "ops", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_wd1_check(unsigned initial_pos, int incr, C &client)
+{
+    incr = std::max(incr, client.nthreads() / 2);
+    unsigned pos = initial_pos + ((client.id() / 2) % incr);
+    unsigned n = 0;
+    Json result = Json();
+
+    double t0 = client.now();
+    if (client.id() % 2 == 0) {
+        unsigned max_remove = -1, min_post_remove = -1, max_post_remove = -1;
+        unsigned bugs = 0;
+        bool found_putpos = false;
+        constexpr int nbatch = 20;
+        Str gotten[nbatch];
+        char gottenbuf[nbatch * 16];
+        for (int i = 0; i < nbatch; ++i)
+            gotten[i].s = &gottenbuf[i * 16];
+
+        while (!client.timeout(0)
+               && (!found_putpos || pos < max_post_remove + 100000)) {
+            for (int i = 0; i < nbatch; ++i) {
+                gotten[i].len = 16;
+                client.get(pos + i * incr, &gotten[i]);
+            }
+            client.wait_all();
+            for (int i = 0; i < nbatch; ++i) {
+                if (gotten[i].len) {
+                    if (min_post_remove == unsigned(-1))
+                        min_post_remove = max_post_remove = pos;
+                    else if (!found_putpos)
+                        max_post_remove = pos;
+                    else if (++bugs == 1)
+                        fprintf(stderr, "%u: present unexpectedly\n", pos);
+                } else {
+                    if (min_post_remove == unsigned(-1))
+                        max_remove = pos;
+                    else
+                        found_putpos = true;
+                }
+                pos += incr;
+            }
+        }
+
+        result.set("removepos", max_remove + incr);
+        result.set("putpos", max_post_remove + incr);
+        if (bugs)
+            result.set("buggykeys", bugs);
+    }
+    client.wait_all();
+    double t1 = client.now();
+
+    kvtest_set_time(result, "ops", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_wd2(C &client)
+{
+    char sbuf[32], kbuf[32], next_kbuf[32];
+    const int sep = 26;
+    const int p_remove = 1000, p_put2 = 10000, p_remove2 = 20000;
+    int x = 0;
+    quick_istr xstr(0);
+
+    client.put(Str("n"), client.nthreads());
+    always_assert(client.nthreads() > 1);
+
+    // set up status keys
+    snprintf(sbuf, sizeof(sbuf), "s%03d", client.id());
+    for (int i = 0; i < sep; ++i) {
+        sbuf[4] = 'A' + i;
+        client.put(Str(sbuf, 5), Str());
+    }
+    client.put(Str(sbuf, 4), xstr.string());
+
+    // set up main keys
+    snprintf(kbuf, sizeof(kbuf), "k%03d", client.id());
+    for (int i = 0; i < sep; ++i) {
+        kbuf[4] = 'A' + i;
+        client.put(Str(kbuf, 5), Str());
+    }
+    client.put(Str(kbuf, 4), Str());
+
+    snprintf(next_kbuf, sizeof(next_kbuf), "k%03d", (client.id() + 1) % client.nthreads());
+
+    // main loop
+    double t0 = client.now();
+    int put_status = 0;
+    long nrounds = 0;
+    while (!client.timeout(0)) {
+        ++nrounds;
+        client.put(Str(kbuf, 4), xstr.string(), &put_status);
+        if ((client.rand.next() % 65536) < p_remove)
+            client.remove(Str(next_kbuf, 4));
+
+        int rand = client.rand.next() % 65536;
+        if (rand < p_put2) {
+            for (int i = sep - 1; i >= 0; --i) {
+                next_kbuf[4] = 'A' + i;
+                client.put(Str(next_kbuf, 5), Str());
+            }
+        } else if (rand < p_remove2) {
+            for (int i = sep - 1; i >= 0; --i) {
+                next_kbuf[4] = 'A' + i;
+                client.remove(Str(next_kbuf, 5));
+            }
+        } else {
+            /* do nothing */
+        }
+
+        client.wait_all();
+
+        if (put_status == Inserted) {
+            ++x;
+            xstr.set(x);
+            client.put(Str(sbuf, 4), xstr.string());
+        }
+    }
+    double t1 = client.now();
+
+    Json result;
+    kvtest_set_time(result, "rounds", nrounds, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_wd2_check(C &client)
+{
+    if (client.id() != 0)
+        return;
+
+    int n;
+    client.get(Str("n"), &n);
+    client.wait_all();
+    always_assert(n > 1);
+    Json result;
+
+    char buf[32];
+    for (int i = 0; i < n; ++i) {
+        int s, k;
+        snprintf(buf, sizeof(buf), "k%03d", i);
+        client.get(Str(buf, 4), &k);
+        snprintf(buf, sizeof(buf), "s%03d", i);
+        client.get(Str(buf, 4), &s);
+        client.wait_all();
+        if (!(s >= 0 && (s == k || s == k + 1 || k == -1)))
+            fprintf(stderr, "problem: s%03d=%d vs. k%03d=%d\n",
+                    i, s, i, k);
+        result.set("thread" + String(i), Json().push_back(s).push_back(k));
+    }
+
+    client.report(result);
+}
+
+// Create a range of keys [initial_pos, initial_pos + n)
+// where key k == initial_pos + i has value (n - 1 - i).
+// Many overwrites.
+template <typename C>
+void kvtest_tri1(unsigned initial_pos, int incr, C &client)
+{
+    incr = std::max(incr, client.nthreads());
+    unsigned n = 0;
+    Json result = Json();
+
+    double t0 = client.now();
+    for (unsigned x = 0; x < client.limit(); ++x)
+        for (unsigned y = 0, z = x; y <= x; ++y, --z, ++n)
+            client.put(initial_pos + y * incr, z);
+    client.wait_all();
+    double t1 = client.now();
+
+    kvtest_set_time(result, "puts", n, t1 - t0);
+    kvtest_set_time(result, "ops", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_tri1_check(unsigned initial_pos, int incr, C &client)
+{
+    incr = std::max(incr, client.nthreads());
+    unsigned n = 0;
+    Json result = Json();
+
+    double t0 = client.now();
+    for (unsigned x = 0; x < client.limit(); ++x, ++n)
+        client.get_check(initial_pos + x * incr, client.limit() - 1 - x);
+    client.wait_all();
+    double t1 = client.now();
+
+    kvtest_set_time(result, "gets", n, t1 - t0);
+    kvtest_set_time(result, "ops", n, t1 - t0);
+    client.report(result);
+}
+
+
+#define PALMN   128000000
+enum { PalmBatch = 8192 / 24 };
+#define PALM_DEBUG 1    // use get_check in palmb, which force palm::get
+                        // to touch the cachline of the value
+template <typename C>
+void kvtest_palma(C &client)
+{
+    Json result = Json();
+    double t0 = client.now();
+    for (int i = 0; i < PALMN; i++) {
+        uint64_t v = i + 1;
+        client.put(i, v);
+    }
+    client.wait_all();
+    double t1 = client.now();
+    kvtest_set_time(result, "ops", PALMN, t1 - t0);
+    client.report(result);
+}
+
+inline int compare_int(const void *a, const void *b)
+{
+    return compare(*(uint64_t *)a, *(uint64_t *)b);
+}
+
+template <typename C>
+void kvtest_palmb_seed(C &client, int seed)
+{
+    Json result = Json();
+    client.rand.reset(seed);
+    double t0 = client.now();
+    int n;
+    int nquery = 0;
+    uint64_t a[PalmBatch];
+    for (n = 0; !client.timeout(0); ++n) {
+        uint64_t x = (uint64_t) client.rand.next();
+        x %= (PALMN / 10);
+        a[nquery++] = x;
+        if (nquery == PalmBatch) {
+            qsort(a, PalmBatch, sizeof(a[0]), compare_int);
+            for (int j = 0; j < PalmBatch && !client.timeout(0); j++) {
+#if PALM_DEBUG
+                uint64_t v = a[j] + 1;
+                client.get_check(a[j], v);
+#else
+                client.get(a[j]);
+#endif
+            }
+            nquery = 0;
+        }
+    }
+    client.wait_all();
+    double t1 = client.now();
+    kvtest_set_time(result, "ops", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_palmb(C &client)
+{
+    kvtest_palmb_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+template <typename C>
+void kvtest_ycsbk_seed(C &client, int seed)
+{
+    client.rand.reset(seed);
+    double tp0 = client.now();
+    int n;
+    char key[512], val[512];
+    for (n = 0; !client.timeout(0) && n < 1000000; ++n) {
+        strcpy(key, "user");
+        int p = 4;
+        for (int i = 0; i < 18; i++, p++)
+            key[p] = '0' + (client.rand.next() % 10);
+        key[p] = 0;
+        int32_t v = (int32_t) client.rand.next();
+        sprintf(val, "%d", v);
+        client.put(Str(key, strlen(key)), Str(val, strlen(val)));
+    }
+    client.wait_all();
+    double tp1 = client.now();
+
+    client.puts_done();
+    client.notice("now getting\n");
+    client.rand.reset(seed);
+    double tg0 = client.now();
+    int g;
+    for (g = 0; g < n && !client.timeout(1); ++g) {
+        strcpy(key, "user");
+        int p = 4;
+        for (int i = 0; i < 18; i++, p++)
+            key[p] = '0' + (client.rand.next() % 10);
+        key[p] = 0;
+        int32_t v = (int32_t) client.rand.next();
+        sprintf(val, "%d", v);
+        client.get_check(Str(key, strlen(key)), Str(val, strlen(val)));
+    }
+    client.wait_all();
+    double tg1 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, tp1 - tp0);
+    kvtest_set_time(result, "gets", g, tg1 - tg0);
+    kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_ycsbk(C &client)
+{
+    kvtest_ycsbk_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+template <typename C>
+void
+kvtest_bdb(C &client)
+{
+    enum { nrec = 500000, keylen = 8, datalen = 32 };
+    char key[keylen + 1];
+    char val[datalen + 1];
+    memset(val, '^', sizeof(val));
+    val[datalen] = 0;
+    key[keylen] = 0;
+    srandom(0);
+    for (int n = 0; n < nrec; n++) {
+        for (int i = 0; i < keylen; i++)
+            key[i] = 'a' + random() % 26;
+        client.put(key, val);
+    }
+    client.wait_all();
+
+    srandom(0);
+    double t0 = now();
+    unsigned long n;
+    for (n = 0; n < 10000000; n++) {
+        for (int i = 0; i < keylen; i++)
+            key[i] = 'a' + random() % 26;
+        client.get_check(key, val);
+        if (n % nrec == 0)
+            srandom(0);
+    }
+    double t1 = now();
+    Json result = Json();
+    kvtest_set_time(result, "ops", n, t1 - t0);
+    client.report(result);
+}
+
+enum { NLongParts = 16 };
+
+template <typename C>
+void
+kvtest_long_init(C &client)
+{
+    assert(client.id() < NLongParts);
+    int seed = kvtest_first_seed + client.id();
+    client.rand.reset(seed);
+    const int keylen = client.keylen();
+    const int prefixLen = client.prefixLen();
+    const char minkltr = client.minkeyletter();
+    const char maxkltr = client.maxkeyletter();
+    assert(prefixLen < keylen);
+    const uint32_t nkeysPerPart = client.nkeys() / NLongParts;
+    char key[512], val[512];
+    val[8] = 0;
+    memset(key, '^', prefixLen);
+    double t0 = now();
+    unsigned long n;
+    for(n = 0; n < nkeysPerPart; ++n){
+        for (int i = prefixLen; i < keylen; i++)
+            key[i] = minkltr + client.rand.next() % (maxkltr - minkltr + 1);
+        key[keylen] = 0;
+        memcpy(val, key + keylen - 8, 8);
+        client.put(key, val);
+        client.rand.next();
+    }
+    client.wait_all();
+    double t1 = now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void
+kvtest_long_go(C &client)
+{
+    const int keylen = client.keylen();
+    const int prefixLen = client.prefixLen();
+    assert(prefixLen < keylen);
+    const uint32_t nKeysPerPart = client.nkeys() / NLongParts;
+    const char minkltr = client.minkeyletter();
+    const char maxkltr = client.maxkeyletter();
+    char key[512], val[512];
+    memset(key, '^', prefixLen);
+    val[8] = 0;
+    double t0 = now();
+    long n = 0;
+    int cur_cid = client.id() % NLongParts;
+    while (!client.timeout(0)) {
+        client.rand.reset(kvtest_first_seed + cur_cid);
+        uint32_t op;
+        for(op = 0; !client.timeout(0) && op < nKeysPerPart; op++){
+            for (int i = prefixLen; i < keylen; i++)
+                key[i] = minkltr + client.rand.next() % (maxkltr - minkltr + 1);
+            memcpy(val, key + keylen - 8, 8);
+            key[keylen] = 0;
+            if (client.rand.next() % 100 < client.getratio())
+                client.get_check(key, val);
+            else
+                client.put(key, val);
+        }
+        cur_cid = (cur_cid + 1) % NLongParts;
+        n += op;
+    }
+    client.wait_all();
+    double t1 = now();
+
+    Json result = Json();
+    kvtest_set_time(result, "ops", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void
+kvtest_wscale(C &client)
+{
+    double t0 = now();
+    client.rand.reset(kvtest_first_seed + client.id() % 48);
+    long n;
+    for(n = 0; !client.timeout(0); n++){
+        long x = client.rand.next();
+        client.put(x, x + 1);
+    }
+    client.wait_all();
+    double t1 = now();
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, t1 -t0);
+    client.report(result);
+}
+
+template <typename C>
+void
+kvtest_ruscale_init(C &client)
+{
+    double t0 = now();
+    client.rand.reset(kvtest_first_seed + client.id() % 48);
+    const int ruscale_partsz = client.ruscale_partsz();
+    const int firstkey = ruscale_partsz * client.ruscale_init_part_no();
+    // Insert in random order
+    int *keys = (int *) malloc(sizeof(int) * ruscale_partsz);
+    always_assert(keys);
+    for(int i = 0; i < ruscale_partsz; i++)
+        keys[i] = i + firstkey;
+    for(int i = 0; i < ruscale_partsz; i++)
+        std::swap(keys[i], keys[client.rand.next() % ruscale_partsz]);
+    for(int i = 0; i < ruscale_partsz; i++){
+        long x = keys[i];
+        client.put(x, x + 1);
+    }
+    client.wait_all();
+    double t1 = now();
+    Json result = Json();
+    kvtest_set_time(result, "puts", ruscale_partsz, t1 - t0);
+    client.report(result);
+    free(keys);
+}
+
+template <typename C>
+void
+kvtest_rscale(C &client)
+{
+    client.rand.reset(kvtest_first_seed + client.id() % 48);
+    const long nseqkeys = client.nseqkeys();
+    double t0 = now();
+    long n;
+    for(n = 0; !client.timeout(0); n++){
+        long x = client.rand.next() % nseqkeys;
+        client.get_check(x, x + 1);
+    }
+    client.wait_all();
+    double t1 = now();
+    Json result = Json();
+    kvtest_set_time(result, "gets", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void
+kvtest_uscale(C &client)
+{
+    client.rand.reset(kvtest_first_seed + client.id());
+    const long nseqkeys = client.nseqkeys();
+    double t0 = now();
+    long n;
+    for(n = 0; !client.timeout(0); n++){
+        long x = client.rand.next() % nseqkeys;
+        client.put(x, x + 1);
+    }
+    client.wait_all();
+    double t1 = now();
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_udp1_seed(C &client, int seed)
+{
+    client.rand.reset(seed);
+    double tp0 = client.now();
+    unsigned n;
+    for (n = 0; !client.timeout(0); ++n)
+        client.put(0, 1);
+    client.wait_all();
+    double tp1 = client.now();
+
+    client.puts_done();
+    client.notice("now getting\n");
+    int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
+    assert(a);
+    client.rand.reset(seed);
+    for (unsigned i = 0; i < n; ++i)
+        a[i] = (int32_t) client.rand.next();
+    for (unsigned i = 0; i < n; ++i)
+        std::swap(a[i], a[client.rand.next() % n]);
+
+    double tg0 = client.now();
+    unsigned g;
+    for (g = 0; !client.timeout(1); ++g)
+        client.get_check(0, 1);
+    client.wait_all();
+    double tg1 = client.now();
+
+    Json result = Json();
+    kvtest_set_time(result, "puts", n, tp1 - tp0);
+    kvtest_set_time(result, "gets", g, tg1 - tg0);
+    kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
+    client.report(result);
+    free(a);
+}
+
+template <typename C>
+void kvtest_udp1(C &client)
+{
+    kvtest_udp1_seed(client, kvtest_first_seed + client.id() % 48);
+}
+
+// do four million of inserts to distinct keys.
+// sometimes overwrites, but only w/ same value.
+// different clients might use same key sometimes.
+template <typename C>
+void kvtest_w1_seed(C &client, int seed)
+{
+    int n;
+    if (client.limit() == ~(uint64_t) 0)
+        n = 4000000;
+    else
+        n = std::min(client.limit(), (uint64_t) INT_MAX);
+    client.rand.reset(seed);
+
+    double t0 = now();
+    for (int i = 0; i < n; i++) {
+        long x = client.rand.next();
+        client.put_key10(x, x + 1);
+    }
+    client.wait_all();
+    double t1 = now();
+
+    Json result = Json().set("total", (long) (n / (t1 - t0)))
+        .set("puts", n)
+        .set("puts_per_sec", n / (t1 - t0));
+    client.report(result);
+}
+
+// do four million gets.
+// in a random order.
+// if we get in the same order that w1 put, performance is
+// about 15% better for b-tree.
+template <typename C>
+void kvtest_r1_seed(C &client, int seed)
+{
+    int n;
+    if (client.limit() == ~(uint64_t) 0)
+        n = 4000000;
+    else
+        n = std::min(client.limit(), (uint64_t) INT_MAX);
+    long *a = (long *) malloc(sizeof(long) * n);
+    always_assert(a);
+
+    client.rand.reset(seed);
+    for (int i = 0; i < n; i++)
+        a[i] = client.rand.next();
+    for (int i = 0; i < n; i++) {
+        int i1 = client.rand.next() % n;
+        long tmp = a[i];
+        a[i] = a[i1];
+        a[i1] = tmp;
+    }
+
+    double t0 = now();
+    for (int i = 0; i < n; i++)
+        client.get_check_key10(a[i], a[i] + 1);
+    client.wait_all();
+    double t1 = now();
+
+    Json result = Json().set("total", (long) (n / (t1 - t0)))
+        .set("gets", n)
+        .set("gets_per_sec", n / (t1 - t0));
+    client.report(result);
+}
+
+// do four million of inserts to distinct keys.
+// sometimes overwrites, but only w/ same value.
+// different clients might use same key sometimes.
+template <typename C>
+void kvtest_wcol1at(C &client, int col, int seed, long maxkeys)
+{
+    int n;
+    if (client.limit() == ~(uint64_t) 0)
+        n = 4000000;
+    else
+        n = std::min(client.limit(), (uint64_t) INT_MAX);
+    client.rand.reset(seed);
+
+    double t0 = now();
+    for (int i = 0; i < n; i++) {
+        long x = client.rand.next() % maxkeys;
+        client.put_col_key10(x, col, x + 1);
+    }
+    client.wait_all();
+    double t1 = now();
+
+    Json result = Json().set("total", (long) (n / (t1 - t0)))
+        .set("puts", n)
+        .set("puts_per_sec", n / (t1 - t0));
+    client.report(result);
+}
+
+// do four million gets.
+// in a random order.
+// if we get in the same order that w1 put, performance is
+// about 15% better for b-tree.
+template <typename C>
+void kvtest_rcol1at(C &client, int col, int seed, long maxkeys)
+{
+    int n;
+    if (client.limit() == ~(uint64_t) 0)
+        n = 4000000;
+    else
+        n = std::min(client.limit(), (uint64_t) INT_MAX);
+    long *a = (long *) malloc(sizeof(long) * n);
+    always_assert(a);
+
+    client.rand.reset(seed);
+    for (int i = 0; i < n; i++)
+        a[i] = client.rand.next() % maxkeys;
+    for (int i = 0; i < n && 0; i++) {
+        int i1 = client.rand.next() % n;
+        long tmp = a[i];
+        a[i] = a[i1];
+        a[i1] = tmp;
+    }
+
+    double t0 = now();
+    for (int i = 0; i < n; i++)
+        client.get_col_check_key10(a[i], col, a[i] + 1);
+    client.wait_all();
+    double t1 = now();
+
+    Json result = Json().set("total", (long) (n / (t1 - t0)))
+        .set("gets", n)
+        .set("gets_per_sec", n / (t1 - t0));
+    client.report(result);
+}
+
+// test scans with parallel inserts
+template <typename C>
+void kvtest_scan1(C &client, double writer_quiet)
+{
+    int n, wq65536 = int(writer_quiet * 65536);
+    if (client.limit() == ~(uint64_t) 0)
+        n = 10000;
+    else
+        n = std::min(client.limit(), (uint64_t) 97655);
+    Json result;
+
+    if (client.id() % 24 == 0) {
+        for (int i = 0; i < n; ++i)
+            client.put_key8(i * 1024, i);
+        client.wait_all();
+
+        int pos = 0, mypos = 0, scansteps = 0;
+        quick_istr key;
+        std::vector<Str> keys, values;
+        Json errj;
+        while (!client.timeout(0) && errj.size() < 1000) {
+            key.set(pos, 8);
+            client.scan_sync(key.string(), 100, keys, values);
+            if (keys.size() == 0) {
+                if (mypos < n * 1024)
+                    errj.push_back("missing " + String(mypos) + " through " + String((n - 1) * 1024));
+                pos = mypos = 0;
+            } else {
+                for (size_t i = 0; i < keys.size(); ++i) {
+                    int val = keys[i].to_i();
+                    if (val < 0) {
+                        errj.push_back("unexpected key " + String(keys[i].s, keys[i].len));
+                        continue;
+                    }
+                    if (val < pos)
+                        errj.push_back("got " + String(keys[i].s, keys[i].len) + ", expected " + String(pos) + " or later");
+                    pos = val + 1;
+                    while (val > mypos) {
+                        errj.push_back("got " + String(keys[i].s, keys[i].len) + ", missing " + String(mypos) + " @" + String(scansteps) + "+" + String(i));
+                        mypos += 1024;
+                    }
+                    if (val == mypos) {
+                        mypos = val + 1024;
+                        ++scansteps;
+                    }
+                }
+            }
+            client.rcu_quiesce();
+        }
+        if (errj.size() >= 1000)
+            errj.push_back("too many errors, giving up");
+        result.set("ok", errj.empty()).set("scansteps", scansteps);
+        if (errj)
+            result.set("errors", errj);
+
+    } else {
+        int delta = 1 + (client.id() % 30) * 32, rounds = 0;
+        while (!client.timeout(0)) {
+            int first = (client.rand.next() % n) * 1024 + delta;
+            int rand = client.rand.next() % 65536;
+            if (rand < wq65536) {
+                for (int d = 0; d < 31; ++d)
+                    relax_fence();
+            } else if (rounds > 100 && (rand % 2) == 1) {
+                for (int d = 0; d < 31; ++d)
+                    client.remove_key8(d + first);
+            } else {
+                for (int d = 0; d < 31; ++d)
+                    client.put_key8(d + first, d + first);
+            }
+            ++rounds;
+            client.rcu_quiesce();
+        }
+    }
+
+    client.report(result);
+}
+
+// test reverse scans with parallel inserts
+template <typename C>
+void kvtest_rscan1(C &client, double writer_quiet)
+{
+    int n, wq65536 = int(writer_quiet * 65536);
+    if (client.limit() == ~(uint64_t) 0)
+        n = 10000;
+    else
+        n = std::min(client.limit(), (uint64_t) 97655);
+    Json result;
+
+    if (client.id() % 24 == 0) {
+        for (int i = 1; i <= n; ++i)
+            client.put_key8(i * 1024, i);
+        client.wait_all();
+
+        int pos = (n + 1) * 1024, mypos = n * 1024, scansteps = 0;
+        quick_istr key;
+        std::vector<Str> keys, values;
+        Json errj;
+        while (!client.timeout(0) && errj.size() < 1000) {
+            key.set(pos, 8);
+            client.rscan_sync(key.string(), 100, keys, values);
+            if (keys.size() == 0) {
+                if (mypos > 0)
+                    errj.push_back("missing 1024 through " + String(mypos) + " @" + String(scansteps));
+                pos = (n + 1) * 1024, mypos = n * 1024;
+            } else {
+                for (size_t i = 0; i < keys.size(); ++i) {
+                    int val = keys[i].to_i();
+                    if (val < 0) {
+                        errj.push_back("unexpected key " + String(keys[i].s, keys[i].len));
+                        continue;
+                    }
+                    if (val > pos)
+                        errj.push_back("got " + String(keys[i].s, keys[i].len) + ", expected " + String(pos) + " or less");
+                    pos = val - 1;
+                    while (val < mypos) {
+                        String last;
+                        if (i)
+                            last = String(keys[i-1].s, keys[i-1].len);
+                        else
+                            last = String(key.string().s, key.string().len);
+                        errj.push_back("got " + String(keys[i].s, keys[i].len) + ", missing " + String(mypos) + " @" + String(scansteps) + "+" + String(i) + ", last " + last);
+                        mypos -= 1024;
+                    }
+                    if (val == mypos) {
+                        mypos = val - 1024;
+                        ++scansteps;
+                    }
+                }
+            }
+            client.rcu_quiesce();
+        }
+        if (errj.size() >= 1000)
+            errj.push_back("too many errors, giving up");
+        result.set("ok", errj.empty()).set("scansteps", scansteps);
+        if (errj)
+            result.set("errors", errj);
+
+    } else {
+        int delta = 1 + (client.id() % 30) * 32, rounds = 0;
+        while (!client.timeout(0)) {
+            int first = (client.rand.next() % n + 1) * 1024 + delta;
+            int rand = client.rand.next() % 65536;
+            if (rand < wq65536) {
+                for (int d = 0; d < 31; ++d)
+                    relax_fence();
+            } else if (rounds > 100 && (rand % 2) == 1) {
+                for (int d = 0; d < 31; ++d)
+                    client.remove_key8(d + first);
+            } else {
+                for (int d = 0; d < 31; ++d)
+                    client.put_key8(d + first, d + first);
+            }
+            ++rounds;
+            client.rcu_quiesce();
+        }
+    }
+
+    client.report(result);
+}
+
+// test concurrent splits with removes in lower layers
+template <typename C>
+void kvtest_splitremove1(C &client)
+{
+    // XXX these parameters depend on masstree constants...
+    int leaf_width = 15, internode_width = 15;
+    int num_keys = leaf_width * (internode_width + 1) + 1;
+    int trigger_key = num_keys - 15;
+    int rounds = 0;
+    Json result, errj;
+
+    if (client.id() == 0) {
+        while (1) {
+            for (int i = 0; i < num_keys; ++i)
+                client.put_key16(i + 100, i + 101);
+            client.rcu_quiesce();
+            for (int i = trigger_key + 1; i < num_keys + 10; ++i)
+                client.remove_key16(i + 100);
+            client.rcu_quiesce();
+            for (int i = 0; i < leaf_width * internode_width; ++i)
+                client.put_key16(i, i + 1);
+
+            client.put(client.nthreads(), client.nthreads() + 1);
+            for (int i = 1; i < client.nthreads(); ++i)
+                client.put(i, i + 1);
+            for (int i = 1; i < client.nthreads(); ++i) {
+                while (!client.timeout(0) && client.get_sync(i))
+                    /* do nothing */;
+            }
+            client.remove_key16(trigger_key);
+            client.remove(client.nthreads());
+            if (client.timeout(0))
+                break;
+
+            for (int i = 0; i < num_keys; ++i) {
+                client.remove_key16(i);
+                client.remove_key16(i + 100);
+            }
+            for (int i = 0; i < 10; ++i)
+                client.rcu_quiesce();
+            ++rounds;
+        }
+
+    } else {
+        quick_istr me(client.id()), trigger(trigger_key, 16);
+        while (1) {
+            while (!client.timeout(0) && !client.get_sync_key16(trigger_key))
+                client.rcu_quiesce();
+            if (client.timeout(0))
+                break;
+
+            for (int i = 0; !client.get_sync(me.string()); ++i) {
+                if (!client.get_sync(trigger.string()) && !client.timeout(0)) {
+                    if (errj.size() == 100)
+                        errj.push_back("more errors");
+                    else if (errj.size() < 100)
+                        errj.push_back("key " + String(trigger.string()) + " missing after " + String(rounds) + " rounds, counter " + String(i));
+                    break;
+                }
+                client.rcu_quiesce();
+            }
+
+            while (!client.timeout(0) && !client.get_sync(me.string()))
+                client.rcu_quiesce();
+            client.remove(me.string());
+            while (!client.timeout(0) && client.get_sync(client.nthreads()))
+                client.rcu_quiesce();
+            if (client.timeout(0))
+                break;
+
+            for (int i = 0; i < 10; ++i)
+                client.rcu_quiesce();
+            ++rounds;
+        }
+    }
+
+    result.set("ok", errj.empty()).set("rounds", rounds);
+    if (errj)
+        result.set("errors", errj);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_url_seed(C &client)
+{
+    if (!client.param("file").is_s()) {
+        client.report(Json::object("ok", false, "error", "need 'file=URLFILE' parameter"));
+        return;
+    }
+
+    std::ifstream infile_url_init(client.param("file").to_s());
+    std::ifstream infile_url_del_get(client.param("file").to_s());
+    std::string ops;
+    std::string url;
+    unsigned count_i = 0;
+    unsigned count_d = 0;
+    unsigned count_g = 0;
+
+    double t0 = client.now();
+    while (count_i < client.limit() && infile_url_init.good()) {
+        //do the following alternately:
+        //insert 10 urls, then delete 5 inserted urls
+        for (int i = 0; i != 10 && infile_url_init >> ops >> url; ++i, ++count_i)
+            client.put(url, 2014);
+        for (int i = 0; i != 5 && infile_url_del_get >> ops >> url; ++i, ++count_d)
+            client.remove(url);
+    }
+    client.wait_all();
+    client.puts_done();
+    double t1 = client.now();
+    infile_url_init.close();
+    client.notice("\ninsert done\n");
+
+    //query all the inserted urls
+    double t2 = client.now();
+    while (count_d + count_g != count_i && infile_url_del_get >> ops >> url) {
+        client.get_check(Str(url), 2014);
+        ++count_g;
+    }
+    client.wait_all();
+    double t3 = client.now();
+
+    // client.notice("Total pool memory: %d\n", client.ti_->poolmem);
+    // client.notice("Total general memory: %d\n", client.ti_->genmem);
+    // client.notice("Total MEMORY: %d\n", client.ti_->poolmem + client.ti_->genmem);
+
+    Json result = Json::object("puts", count_i, "removes", count_d);
+    kvtest_set_time(result, "gets", count_g, t3 - t2);
+    kvtest_set_time(result, "ops", count_i + count_d, t1 - t0);
+    client.report(result);
+}
+
+template <typename C>
+void kvtest_url(C &client)
+{
+  kvtest_url_seed(client);
+}
+
+#endif
diff --git a/silo/masstree/kvthread.cc b/silo/masstree/kvthread.cc
new file mode 100644 (file)
index 0000000..fdef8c6
--- /dev/null
@@ -0,0 +1,315 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "kvthread.hh"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <new>
+#include <sys/mman.h>
+#if HAVE_SUPERPAGE && !NOSUPERPAGE
+#include <sys/types.h>
+#include <dirent.h>
+#endif
+
+threadinfo *threadinfo::allthreads;
+pthread_key_t threadinfo::key;
+#if ENABLE_ASSERTIONS
+int threadinfo::no_pool_value;
+#endif
+
+#if HAVE_MEMDEBUG
+void memdebug::landmark(char* buf, size_t size) const {
+    if (this->magic != magic_value && this->magic != magic_free_value)
+        snprintf(buf, size, "???");
+    else if (this->file)
+        snprintf(buf, size, "%s:%d", this->file, this->line);
+    else if (this->line)
+        snprintf(buf, size, "%d", this->line);
+    else
+        snprintf(buf, size, "0");
+}
+
+void
+memdebug::hard_free_checks(const memdebug *m, size_t size, int freetype,
+                           int after_rcu, const char *op) {
+    char buf[256];
+    m->landmark(buf, sizeof(buf));
+    if (m->magic == magic_free_value)
+        fprintf(stderr, "%s(%p): double free, was @%s\n",
+                op, m + 1, buf);
+    else if (m->magic != magic_value)
+        fprintf(stderr, "%s(%p): freeing unallocated pointer (%x)\n",
+                op, m + 1, m->magic);
+    assert(m->magic == magic_value);
+    if (freetype && m->freetype != freetype)
+        fprintf(stderr, "%s(%p): expected type %x, saw %x, "
+                "allocated %s\n", op, m + 1, freetype, m->freetype, buf);
+    if (!after_rcu && m->size != size)
+        fprintf(stderr, "%s(%p): expected size %lu, saw %lu, "
+                "allocated %s\n", op, m + 1,
+                (unsigned long) size, (unsigned long) m->size, buf);
+    if (m->after_rcu != after_rcu)
+        fprintf(stderr, "%s(%p): double free after rcu, allocated @%s\n",
+                op, m + 1, buf);
+    if (freetype)
+        assert(m->freetype == freetype);
+    if (!after_rcu)
+        assert(m->size == size);
+    assert(m->after_rcu == after_rcu);
+}
+
+void
+memdebug::hard_assert_use(const void *ptr, memtag tag1, memtag tag2) {
+    const memdebug *m = reinterpret_cast<const memdebug *>(ptr) - 1;
+    char tagbuf[40], buf[256];
+    m->landmark(buf, sizeof(buf));
+    if (tag2 == (memtag) -1)
+        sprintf(buf, "%x", tag1);
+    else
+        sprintf(buf, "%x/%x", tag1, tag2);
+    if (m->magic == magic_free_value)
+        fprintf(stderr, "%p: use tag %s after free, allocated %s\n",
+                m + 1, tagbuf, buf);
+    else if (m->magic != magic_value)
+        fprintf(stderr, "%p: pointer is unallocated, not tag %s\n",
+                m + 1, tagbuf);
+    assert(m->magic == magic_value);
+    if (tag1 != 0 && (m->freetype >> 8) != tag1 && (m->freetype >> 8) != tag2)
+        fprintf(stderr, "%p: expected tag %s, got tag %x, allocated %s\n",
+                m + 1, tagbuf, m->freetype >> 8, buf);
+    if (tag1 != 0)
+        assert((m->freetype >> 8) == tag1 || (m->freetype >> 8) == tag2);
+}
+#endif
+
+threadinfo *threadinfo::make(int purpose, int index) {
+    static int threads_initialized;
+
+    threadinfo *ti = (threadinfo *) malloc(8192);
+    memset(ti, 0, sizeof(*ti));
+    ti->next_ = allthreads;
+    ti->purpose_ = purpose;
+    ti->index_ = index;
+    ti->allthreads = ti;
+    ti->ts_ = 2;
+    void *limbo_space = ti->allocate(sizeof(limbo_group), memtag_limbo);
+    ti->mark(tc_limbo_slots, limbo_group::capacity);
+    ti->limbo_head_ = ti->limbo_tail_ = new(limbo_space) limbo_group;
+
+    if (!threads_initialized) {
+#if ENABLE_ASSERTIONS
+        const char* s = getenv("_");
+        no_pool_value = s && strstr(s, "valgrind") != 0;
+#endif
+        threads_initialized = 1;
+    }
+
+    return ti;
+}
+
+void threadinfo::refill_rcu()
+{
+    if (limbo_head_ == limbo_tail_ && !limbo_tail_->next_
+        && limbo_tail_->head_ == limbo_tail_->tail_)
+        limbo_tail_->head_ = limbo_tail_->tail_ = 0;
+    else if (!limbo_tail_->next_) {
+        void *limbo_space = allocate(sizeof(limbo_group), memtag_limbo);
+        mark(tc_limbo_slots, limbo_group::capacity);
+        limbo_tail_->next_ = new(limbo_space) limbo_group;
+        limbo_tail_ = limbo_tail_->next_;
+    } else
+        limbo_tail_ = limbo_tail_->next_;
+}
+
+void threadinfo::hard_rcu_quiesce()
+{
+    uint64_t min_epoch = gc_epoch_;
+    for (threadinfo *ti = allthreads; ti; ti = ti->next()) {
+        prefetch((const void *) ti->next());
+        uint64_t epoch = ti->gc_epoch_;
+        if (epoch && (int64_t) (epoch - min_epoch) < 0)
+            min_epoch = epoch;
+    }
+
+    limbo_group *lg = limbo_head_;
+    limbo_element *lb = &lg->e_[lg->head_];
+    limbo_element *le = &lg->e_[lg->tail_];
+
+    if (lb != le && (int64_t) (lb->epoch_ - min_epoch) < 0) {
+        while (1) {
+            free_rcu(lb->ptr_, lb->freetype_);
+            mark(tc_gc);
+
+            ++lb;
+
+            if (lb == le && lg == limbo_tail_) {
+                lg->head_ = lg->tail_;
+                break;
+            } else if (lb == le) {
+                assert(lg->tail_ == lg->capacity && lg->next_);
+                lg->head_ = lg->tail_ = 0;
+                lg = lg->next_;
+                lb = &lg->e_[lg->head_];
+                le = &lg->e_[lg->tail_];
+            } else if (lb->epoch_ < min_epoch) {
+                lg->head_ = lb - lg->e_;
+                break;
+            }
+        }
+
+        if (lg != limbo_head_) {
+            // shift nodes in [limbo_head_, limbo_tail_) to be after
+            // limbo_tail_
+            limbo_group *old_head = limbo_head_;
+            limbo_head_ = lg;
+            limbo_group **last = &limbo_tail_->next_;
+            while (*last)
+                last = &(*last)->next_;
+            *last = old_head;
+            while (*last != lg)
+                last = &(*last)->next_;
+            *last = 0;
+        }
+    }
+
+    limbo_epoch_ = (lb == le ? 0 : lb->epoch_);
+}
+
+void threadinfo::report_rcu(void *ptr) const
+{
+    for (limbo_group *lg = limbo_head_; lg; lg = lg->next_) {
+        int status = 0;
+        for (int i = 0; i < lg->capacity; ++i) {
+            if (i == lg->head_)
+                status = 1;
+            if (i == lg->tail_)
+                status = 0;
+            if (lg->e_[i].ptr_ == ptr)
+                fprintf(stderr, "thread %d: rcu %p@%d: %s as %x @%" PRIu64 "\n",
+                        index_, lg, i, status ? "waiting" : "freed",
+                        lg->e_[i].freetype_, lg->e_[i].epoch_);
+        }
+    }
+}
+
+void threadinfo::report_rcu_all(void *ptr)
+{
+    for (threadinfo *ti = allthreads; ti; ti = ti->next())
+        ti->report_rcu(ptr);
+}
+
+
+#if HAVE_SUPERPAGE && !NOSUPERPAGE
+static size_t read_superpage_size() {
+    if (DIR* d = opendir("/sys/kernel/mm/hugepages")) {
+        size_t n = (size_t) -1;
+        while (struct dirent* de = readdir(d))
+            if (de->d_type == DT_DIR
+                && strncmp(de->d_name, "hugepages-", 10) == 0
+                && de->d_name[10] >= '0' && de->d_name[10] <= '9') {
+                size_t x = strtol(&de->d_name[10], 0, 10) << 10;
+                n = (x < n ? x : n);
+            }
+        closedir(d);
+        return n;
+    } else
+        return 2 << 20;
+}
+
+static size_t superpage_size = 0;
+#endif
+
+static void initialize_pool(void* pool, size_t sz, size_t unit) {
+    char* p = reinterpret_cast<char*>(pool);
+    void** nextptr = reinterpret_cast<void**>(p);
+    for (size_t off = unit; off + unit <= sz; off += unit) {
+        *nextptr = p + off;
+        nextptr = reinterpret_cast<void**>(p + off);
+    }
+    *nextptr = 0;
+}
+
+void threadinfo::refill_pool(int nl) {
+    assert(!pool_[nl - 1]);
+
+    if (!use_pool()) {
+        pool_[nl - 1] = malloc(nl * CACHE_LINE_SIZE);
+        if (pool_[nl - 1])
+            *reinterpret_cast<void**>(pool_[nl - 1]) = 0;
+        return;
+    }
+
+    void* pool = 0;
+    size_t pool_size = 0;
+    int r;
+
+#if HAVE_SUPERPAGE && !NOSUPERPAGE
+    if (!superpage_size)
+        superpage_size = read_superpage_size();
+    if (superpage_size != (size_t) -1) {
+        pool_size = superpage_size;
+# if MADV_HUGEPAGE
+        if ((r = posix_memalign(&pool, pool_size, pool_size)) != 0) {
+            fprintf(stderr, "posix_memalign superpage: %s\n", strerror(r));
+            pool = 0;
+            superpage_size = (size_t) -1;
+        } else if (madvise(pool, pool_size, MADV_HUGEPAGE) != 0) {
+            perror("madvise superpage");
+            superpage_size = (size_t) -1;
+        }
+# elif MAP_HUGETLB
+        pool = mmap(0, pool_size, PROT_READ | PROT_WRITE,
+                    MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+        if (pool == MAP_FAILED) {
+            perror("mmap superpage");
+            pool = 0;
+            superpage_size = (size_t) -1;
+        }
+# else
+        superpage_size = (size_t) -1;
+# endif
+    }
+#endif
+
+    if (!pool) {
+        pool_size = 2 << 20;
+        if ((r = posix_memalign(&pool, CACHE_LINE_SIZE, pool_size)) != 0) {
+            fprintf(stderr, "posix_memalign: %s\n", strerror(r));
+            abort();
+        }
+    }
+
+    initialize_pool(pool, pool_size, nl * CACHE_LINE_SIZE);
+    pool_[nl - 1] = pool;
+}
+
+void threadinfo::run() {
+    threadid_ = pthread_self();
+    pthread_setspecific(key, this);
+}
+
+void* threadinfo::thread_trampoline(void* argument) {
+    threadinfo* ti = static_cast<threadinfo*>(argument);
+    ti->run();
+    return ti->thread_func_(ti);
+}
+
+int threadinfo::run(void* (*thread_func)(threadinfo*), void* thread_data) {
+    assert(!thread_func_ && !threadid_);
+    thread_func_ = thread_func;
+    thread_data_ = thread_data;
+    return pthread_create(&threadid_, 0, thread_trampoline, this);
+}
diff --git a/silo/masstree/kvthread.hh b/silo/masstree/kvthread.hh
new file mode 100644 (file)
index 0000000..63657a3
--- /dev/null
@@ -0,0 +1,477 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVTHREAD_HH
+#define KVTHREAD_HH 1
+#include "mtcounters.hh"
+#include "compiler.hh"
+#include "circular_int.hh"
+#include "timestamp.hh"
+#include <assert.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+
+class threadinfo;
+class loginfo;
+
+extern volatile uint64_t globalepoch;    // global epoch, updated regularly
+extern volatile bool recovering;
+
+struct memdebug {
+#if HAVE_MEMDEBUG
+    enum {
+        magic_value = 389612313 /* = 0x17390319 */,
+        magic_free_value = 2015593488 /* = 0x78238410 */
+    };
+    int magic;
+    int freetype;
+    size_t size;
+    int after_rcu;
+    int line;
+    const char* file;
+
+    static void* make(void* p, size_t size, int freetype) {
+        if (p) {
+            memdebug *m = reinterpret_cast<memdebug *>(p);
+            m->magic = magic_value;
+            m->freetype = freetype;
+            m->size = size;
+            m->after_rcu = 0;
+            m->line = 0;
+            m->file = 0;
+            return m + 1;
+        } else
+            return p;
+    }
+    static void set_landmark(void* p, const char* file, int line) {
+        if (p) {
+            memdebug* m = reinterpret_cast<memdebug*>(p) - 1;
+            m->file = file;
+            m->line = line;
+        }
+    }
+    static void *check_free(void *p, size_t size, int freetype) {
+        memdebug *m = reinterpret_cast<memdebug *>(p) - 1;
+        free_checks(m, size, freetype, false, "deallocate");
+        m->magic = magic_free_value;
+        return m;
+    }
+    static void check_rcu(void *p, size_t size, int freetype) {
+        memdebug *m = reinterpret_cast<memdebug *>(p) - 1;
+        free_checks(m, size, freetype, false, "deallocate_rcu");
+        m->after_rcu = 1;
+    }
+    static void *check_free_after_rcu(void *p, int freetype) {
+        memdebug *m = reinterpret_cast<memdebug *>(p) - 1;
+        free_checks(m, 0, freetype, true, "free_after_rcu");
+        m->magic = magic_free_value;
+        return m;
+    }
+    static bool check_use(const void *p, int type) {
+        const memdebug *m = reinterpret_cast<const memdebug *>(p) - 1;
+        return m->magic == magic_value && (type == 0 || (m->freetype >> 8) == type);
+    }
+    static bool check_use(const void *p, int type1, int type2) {
+        const memdebug *m = reinterpret_cast<const memdebug *>(p) - 1;
+        return m->magic == magic_value
+            && ((m->freetype >> 8) == type1 || (m->freetype >> 8) == type2);
+    }
+    static void assert_use(const void *p, memtag tag) {
+        if (!check_use(p, tag))
+            hard_assert_use(p, tag, (memtag) -1);
+    }
+    static void assert_use(const void *p, memtag tag1, memtag tag2) {
+        if (!check_use(p, tag1, tag2))
+            hard_assert_use(p, tag1, tag2);
+    }
+  private:
+    static void free_checks(const memdebug *m, size_t size, int freetype,
+                            int after_rcu, const char *op) {
+        if (m->magic != magic_value
+            || m->freetype != freetype
+            || (!after_rcu && m->size != size)
+            || m->after_rcu != after_rcu)
+            hard_free_checks(m, freetype, size, after_rcu, op);
+    }
+    void landmark(char* buf, size_t size) const;
+    static void hard_free_checks(const memdebug* m, size_t size, int freetype,
+                                 int after_rcu, const char* op);
+    static void hard_assert_use(const void* ptr, memtag tag1, memtag tag2);
+#else
+    static void *make(void *p, size_t, int) {
+        return p;
+    }
+    static void set_landmark(void*, const char*, int) {
+    }
+    static void *check_free(void *p, size_t, int) {
+        return p;
+    }
+    static void check_rcu(void *, size_t, int) {
+    }
+    static void *check_free_after_rcu(void *p, int) {
+        return p;
+    }
+    static bool check_use(void *, memtag) {
+        return true;
+    }
+    static bool check_use(void *, memtag, memtag) {
+        return true;
+    }
+    static void assert_use(void *, memtag) {
+    }
+    static void assert_use(void *, memtag, memtag) {
+    }
+#endif
+};
+
+enum {
+#if HAVE_MEMDEBUG
+    memdebug_size = sizeof(memdebug)
+#else
+    memdebug_size = 0
+#endif
+};
+
+struct limbo_element {
+    void *ptr_;
+    int freetype_;
+    uint64_t epoch_;
+};
+
+struct limbo_group {
+    enum { capacity = (4076 - sizeof(limbo_group *)) / sizeof(limbo_element) };
+    int head_;
+    int tail_;
+    limbo_element e_[capacity];
+    limbo_group *next_;
+    limbo_group()
+        : head_(0), tail_(0), next_() {
+    }
+    void push_back(void *ptr, int freetype, uint64_t epoch) {
+        assert(tail_ < capacity);
+        e_[tail_].ptr_ = ptr;
+        e_[tail_].freetype_ = freetype;
+        e_[tail_].epoch_ = epoch;
+        ++tail_;
+    }
+};
+
+template <int N> struct has_threadcounter {
+    static bool test(threadcounter ci) {
+        return unsigned(ci) < unsigned(N);
+    }
+};
+template <> struct has_threadcounter<0> {
+    static bool test(threadcounter) {
+        return false;
+    }
+};
+
+struct rcu_callback {
+    virtual ~rcu_callback() {
+    }
+    virtual void operator()(threadinfo& ti) = 0;
+};
+
+class threadinfo {
+  public:
+    enum {
+        TI_MAIN, TI_PROCESS, TI_LOG, TI_CHECKPOINT
+    };
+
+    static threadinfo *make(int purpose, int index);
+    // XXX destructor
+    static pthread_key_t key;
+
+    // thread information
+    int purpose() const {
+        return purpose_;
+    }
+    int index() const {
+        return index_;
+    }
+    loginfo* logger() const {
+        return logger_;
+    }
+    void set_logger(loginfo* logger) {
+        assert(!logger_ && logger);
+        logger_ = logger;
+    }
+    static threadinfo *allthreads;
+    threadinfo* next() const {
+        return next_;
+    }
+
+    // timestamps
+    kvtimestamp_t operation_timestamp() const {
+        return timestamp();
+    }
+    kvtimestamp_t update_timestamp() const {
+        return ts_;
+    }
+    kvtimestamp_t update_timestamp(kvtimestamp_t x) const {
+        if (circular_int<kvtimestamp_t>::less_equal(ts_, x))
+            // x might be a marker timestamp; ensure result is not
+            ts_ = (x | 1) + 1;
+        return ts_;
+    }
+    kvtimestamp_t update_timestamp(kvtimestamp_t x, kvtimestamp_t y) const {
+        if (circular_int<kvtimestamp_t>::less(x, y))
+            x = y;
+        if (circular_int<kvtimestamp_t>::less_equal(ts_, x))
+            // x might be a marker timestamp; ensure result is not
+            ts_ = (x | 1) + 1;
+        return ts_;
+    }
+    void increment_timestamp() {
+        ts_ += 2;
+    }
+    void advance_timestamp(kvtimestamp_t x) {
+        if (circular_int<kvtimestamp_t>::less(ts_, x))
+            ts_ = x;
+    }
+
+    // event counters
+    void mark(threadcounter ci) {
+        if (has_threadcounter<int(ncounters)>::test(ci))
+            ++counters_[ci];
+    }
+    void mark(threadcounter ci, int64_t delta) {
+        if (has_threadcounter<int(ncounters)>::test(ci))
+            counters_[ci] += delta;
+    }
+    bool has_counter(threadcounter ci) const {
+        return has_threadcounter<int(ncounters)>::test(ci);
+    }
+    uint64_t counter(threadcounter ci) const {
+        return has_threadcounter<int(ncounters)>::test(ci) ? counters_[ci] : 0;
+    }
+
+    struct accounting_relax_fence_function {
+        threadinfo *ti_;
+        threadcounter ci_;
+        accounting_relax_fence_function(threadinfo *ti, threadcounter ci)
+            : ti_(ti), ci_(ci) {
+        }
+        void operator()() {
+            relax_fence();
+            ti_->mark(ci_);
+        }
+    };
+    /** @brief Return a function object that calls mark(ci); relax_fence().
+     *
+     * This function object can be used to count the number of relax_fence()s
+     * executed. */
+    accounting_relax_fence_function accounting_relax_fence(threadcounter ci) {
+        return accounting_relax_fence_function(this, ci);
+    }
+
+    struct stable_accounting_relax_fence_function {
+        threadinfo *ti_;
+        stable_accounting_relax_fence_function(threadinfo *ti)
+            : ti_(ti) {
+        }
+        template <typename V>
+        void operator()(V v) {
+            relax_fence();
+            ti_->mark(threadcounter(tc_stable + (v.isleaf() << 1) + v.splitting()));
+        }
+    };
+    /** @brief Return a function object that calls mark(ci); relax_fence().
+     *
+     * This function object can be used to count the number of relax_fence()s
+     * executed. */
+    stable_accounting_relax_fence_function stable_fence() {
+        return stable_accounting_relax_fence_function(this);
+    }
+
+    accounting_relax_fence_function lock_fence(threadcounter ci) {
+        return accounting_relax_fence_function(this, ci);
+    }
+
+    // memory allocation
+    void* allocate(size_t sz, memtag tag) {
+        void *p = malloc(sz + memdebug_size);
+        p = memdebug::make(p, sz, tag << 8);
+        if (p)
+            mark(threadcounter(tc_alloc + (tag > memtag_value)), sz);
+        return p;
+    }
+    void deallocate(void* p, size_t sz, memtag tag) {
+        // in C++ allocators, 'p' must be nonnull
+        assert(p);
+        p = memdebug::check_free(p, sz, tag << 8);
+        free(p);
+        mark(threadcounter(tc_alloc + (tag > memtag_value)), -sz);
+    }
+    void deallocate_rcu(void *p, size_t sz, memtag tag) {
+        assert(p);
+        memdebug::check_rcu(p, sz, tag << 8);
+        record_rcu(p, tag << 8);
+        mark(threadcounter(tc_alloc + (tag > memtag_value)), -sz);
+    }
+
+    void* pool_allocate(size_t sz, memtag tag) {
+        int nl = (sz + memdebug_size + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE;
+        assert(nl <= pool_max_nlines);
+        if (unlikely(!pool_[nl - 1]))
+            refill_pool(nl);
+        void *p = pool_[nl - 1];
+        if (p) {
+            pool_[nl - 1] = *reinterpret_cast<void **>(p);
+            p = memdebug::make(p, sz, (tag << 8) + nl);
+            mark(threadcounter(tc_alloc + (tag > memtag_value)),
+                 nl * CACHE_LINE_SIZE);
+        }
+        return p;
+    }
+    void pool_deallocate(void* p, size_t sz, memtag tag) {
+        int nl = (sz + memdebug_size + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE;
+        assert(p && nl <= pool_max_nlines);
+        p = memdebug::check_free(p, sz, (tag << 8) + nl);
+        if (use_pool()) {
+            *reinterpret_cast<void **>(p) = pool_[nl - 1];
+            pool_[nl - 1] = p;
+        } else
+            free(p);
+        mark(threadcounter(tc_alloc + (tag > memtag_value)),
+             -nl * CACHE_LINE_SIZE);
+    }
+    void pool_deallocate_rcu(void* p, size_t sz, memtag tag) {
+        int nl = (sz + memdebug_size + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE;
+        assert(p && nl <= pool_max_nlines);
+        memdebug::check_rcu(p, sz, (tag << 8) + nl);
+        record_rcu(p, (tag << 8) + nl);
+        mark(threadcounter(tc_alloc + (tag > memtag_value)),
+             -nl * CACHE_LINE_SIZE);
+    }
+
+    // RCU
+    void rcu_start() {
+        if (gc_epoch_ != globalepoch)
+            gc_epoch_ = globalepoch;
+    }
+    void rcu_stop() {
+        if (limbo_epoch_ && (gc_epoch_ - limbo_epoch_) > 1)
+            hard_rcu_quiesce();
+        gc_epoch_ = 0;
+    }
+    void rcu_quiesce() {
+        rcu_start();
+        if (limbo_epoch_ && (gc_epoch_ - limbo_epoch_) > 2)
+            hard_rcu_quiesce();
+    }
+    typedef ::rcu_callback rcu_callback;
+    void rcu_register(rcu_callback* cb) {
+        record_rcu(cb, -1);
+    }
+
+    // thread management
+    void run();
+    int run(void* (*thread_func)(threadinfo*), void* thread_data = 0);
+    pthread_t threadid() const {
+        return threadid_;
+    }
+    void* thread_data() const {
+        return thread_data_;
+    }
+
+    static threadinfo *current() {
+        return (threadinfo *) pthread_getspecific(key);
+    }
+
+    void report_rcu(void *ptr) const;
+    static void report_rcu_all(void *ptr);
+
+  private:
+    union {
+        struct {
+            uint64_t gc_epoch_;
+            uint64_t limbo_epoch_;
+            loginfo *logger_;
+
+            threadinfo *next_;
+            int purpose_;
+            int index_;         // the index of a udp, logging, tcp,
+                                // checkpoint or recover thread
+
+            pthread_t threadid_;
+        };
+        char padding1[CACHE_LINE_SIZE];
+    };
+
+  private:
+    enum { pool_max_nlines = 20 };
+    void *pool_[pool_max_nlines];
+
+    limbo_group *limbo_head_;
+    limbo_group *limbo_tail_;
+    mutable kvtimestamp_t ts_;
+
+    //enum { ncounters = (int) tc_max };
+    enum { ncounters = 0 };
+    uint64_t counters_[ncounters];
+
+    void* (*thread_func_)(threadinfo*);
+    void* thread_data_;
+
+    void refill_pool(int nl);
+    void refill_rcu();
+
+    void free_rcu(void *p, int freetype) {
+        if ((freetype & 255) == 0) {
+            p = memdebug::check_free_after_rcu(p, freetype);
+            ::free(p);
+        } else if (freetype == -1)
+            (*static_cast<rcu_callback *>(p))(*this);
+        else {
+            p = memdebug::check_free_after_rcu(p, freetype);
+            int nl = freetype & 255;
+            *reinterpret_cast<void **>(p) = pool_[nl - 1];
+            pool_[nl - 1] = p;
+        }
+    }
+
+    void record_rcu(void* ptr, int freetype) {
+        if (recovering && freetype == (memtag_value << 8)) {
+            free_rcu(ptr, freetype);
+            return;
+        }
+        if (limbo_tail_->tail_ == limbo_tail_->capacity)
+            refill_rcu();
+        uint64_t epoch = globalepoch;
+        limbo_tail_->push_back(ptr, freetype, epoch);
+        if (!limbo_epoch_)
+            limbo_epoch_ = epoch;
+    }
+
+#if ENABLE_ASSERTIONS
+    static int no_pool_value;
+    static bool use_pool() {
+        return !no_pool_value;
+    }
+#else
+    static bool use_pool() {
+        return true;
+    }
+#endif
+
+    void hard_rcu_quiesce();
+    static void* thread_trampoline(void*);
+    friend class loginfo;
+};
+
+#endif
diff --git a/silo/masstree/local_vector.hh b/silo/masstree/local_vector.hh
new file mode 100644 (file)
index 0000000..d36764b
--- /dev/null
@@ -0,0 +1,337 @@
+#ifndef GSTORE_LOCAL_VECTOR_HH
+#define GSTORE_LOCAL_VECTOR_HH 1
+#include "compiler.hh"
+#include <memory>
+#include <iterator>
+#include <assert.h>
+
+template <typename T, int N, typename A = std::allocator<T> >
+class local_vector {
+  public:
+    typedef bool (local_vector<T, N, A>::*unspecified_bool_type)() const;
+    typedef T value_type;
+    typedef value_type* iterator;
+    typedef const value_type* const_iterator;
+    typedef std::reverse_iterator<iterator> reverse_iterator;
+    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+    typedef unsigned size_type;
+
+    inline local_vector(const A& allocator = A());
+    local_vector(const local_vector<T, N, A>& x);
+    template <int NN, typename AA>
+    local_vector(const local_vector<T, NN, AA>& x);
+    inline ~local_vector();
+
+    inline size_type size() const;
+    inline size_type capacity() const;
+    inline bool empty() const;
+    inline operator unspecified_bool_type() const;
+    inline bool operator!() const;
+
+    inline iterator begin();
+    inline iterator end();
+    inline const_iterator begin() const;
+    inline const_iterator end() const;
+    inline const_iterator cbegin() const;
+    inline const_iterator cend() const;
+    inline reverse_iterator rbegin();
+    inline reverse_iterator rend();
+    inline const_reverse_iterator rbegin() const;
+    inline const_reverse_iterator rend() const;
+    inline const_reverse_iterator crbegin() const;
+    inline const_reverse_iterator crend() const;
+
+    inline value_type& operator[](size_type i);
+    inline const value_type& operator[](size_type i) const;
+    inline value_type& front();
+    inline const value_type& front() const;
+    inline value_type& back();
+    inline const value_type& back() const;
+
+    inline void push_back(const value_type& x);
+    inline void push_back(value_type&& x);
+    template <typename... Args> inline void emplace_back(Args&&... args);
+    inline void pop_back();
+
+    inline void clear();
+    inline void resize(size_type n, value_type x = value_type());
+    iterator erase(iterator position);
+    iterator erase(iterator first, iterator last);
+
+    inline local_vector<T, N, A>& operator=(const local_vector<T, N, A>& x);
+    template <int NN, typename AA>
+    inline local_vector<T, N, A>& operator=(const local_vector<T, NN, AA>& x);
+
+  private:
+    struct rep : public A {
+        T* first_;
+        T* last_;
+        T* capacity_;
+        char lv_[sizeof(T) * N]; // XXX does not obey alignof(T)
+
+        inline rep(const A& a);
+    };
+    rep r_;
+
+    void grow(size_type n = 0);
+};
+
+template <typename T, int N, typename A>
+inline local_vector<T, N, A>::rep::rep(const A& a)
+    : A(a), first_(reinterpret_cast<T*>(lv_)),
+      last_(first_), capacity_(first_ + N) {
+}
+
+template <typename T, int N, typename A>
+inline local_vector<T, N, A>::local_vector(const A& allocator)
+    : r_(allocator) {
+}
+
+template <typename T, int N, typename A>
+local_vector<T, N, A>::local_vector(const local_vector<T, N, A>& x)
+    : r_(A()) {
+    for (const T* it = x.r_.first_; it != x.r_.last_; ++it)
+        push_back(*it);
+}
+
+template <typename T, int N, typename A> template <int NN, typename AA>
+local_vector<T, N, A>::local_vector(const local_vector<T, NN, AA>& x)
+    : r_(A()) {
+    for (const T* it = x.r_.first_; it != x.r_.last_; ++it)
+        push_back(*it);
+}
+
+template <typename T, int N, typename A>
+inline local_vector<T, N, A>::~local_vector() {
+    for (T* it = r_.first_; it != r_.last_; ++it)
+        r_.destroy(it);
+    if (r_.first_ != reinterpret_cast<T*>(r_.lv_))
+        r_.deallocate(r_.first_, r_.capacity_ - r_.first_);
+}
+
+template <typename T, int N, typename A>
+inline unsigned local_vector<T, N, A>::size() const {
+    return r_.last_ - r_.first_;
+}
+
+template <typename T, int N, typename A>
+inline unsigned local_vector<T, N, A>::capacity() const {
+    return r_.capacity_ - r_.first_;
+}
+
+template <typename T, int N, typename A>
+inline bool local_vector<T, N, A>::empty() const {
+    return r_.first_ == r_.last_;
+}
+
+template <typename T, int N, typename A>
+inline local_vector<T, N, A>::operator unspecified_bool_type() const {
+    return empty() ? 0 : &local_vector<T, N, A>::empty;
+}
+
+template <typename T, int N, typename A>
+inline bool local_vector<T, N, A>::operator!() const {
+    return empty();
+}
+
+template <typename T, int N, typename A>
+void local_vector<T, N, A>::grow(size_type n) {
+    size_t newcap = capacity() * 2;
+    while (newcap < n)
+        newcap *= 2;
+    T* m = r_.allocate(newcap);
+    for (T* it = r_.first_, *mit = m; it != r_.last_; ++it, ++mit) {
+        r_.construct(mit, std::move(*it));
+        r_.destroy(it);
+    }
+    if (r_.first_ != reinterpret_cast<T*>(r_.lv_))
+        r_.deallocate(r_.first_, capacity());
+    r_.last_ = m + (r_.last_ - r_.first_);
+    r_.first_ = m;
+    r_.capacity_ = m + newcap;
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::begin() -> iterator {
+    return r_.first_;
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::end() -> iterator {
+    return r_.last_;
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::begin() const -> const_iterator {
+    return r_.first_;
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::end() const -> const_iterator {
+    return r_.last_;
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::cbegin() const -> const_iterator {
+    return r_.first_;
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::cend() const -> const_iterator {
+    return r_.last_;
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::rbegin() -> reverse_iterator {
+    return reverse_iterator(end());
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::rend() -> reverse_iterator {
+    return reverse_iterator(begin());
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::rbegin() const -> const_reverse_iterator {
+    return const_reverse_iterator(end());
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::rend() const -> const_reverse_iterator {
+    return const_reverse_iterator(begin());
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::crbegin() const -> const_reverse_iterator {
+    return const_reverse_iterator(end());
+}
+
+template <typename T, int N, typename A>
+inline auto local_vector<T, N, A>::crend() const -> const_reverse_iterator {
+    return const_reverse_iterator(begin());
+}
+
+template <typename T, int N, typename A>
+inline T& local_vector<T, N, A>::operator[](size_type i) {
+    return r_.first_[i];
+}
+
+template <typename T, int N, typename A>
+inline const T& local_vector<T, N, A>::operator[](size_type i) const {
+    return r_.first_[i];
+}
+
+template <typename T, int N, typename A>
+inline T& local_vector<T, N, A>::front() {
+    return r_.first_[0];
+}
+
+template <typename T, int N, typename A>
+inline const T& local_vector<T, N, A>::front() const {
+    return r_.first_[0];
+}
+
+template <typename T, int N, typename A>
+inline T& local_vector<T, N, A>::back() {
+    return r_.last_[-1];
+}
+
+template <typename T, int N, typename A>
+inline const T& local_vector<T, N, A>::back() const {
+    return r_.last_[-1];
+}
+
+template <typename T, int N, typename A>
+inline void local_vector<T, N, A>::push_back(const T& x) {
+    if (r_.last_ == r_.capacity_)
+        grow();
+    r_.construct(r_.last_, x);
+    ++r_.last_;
+}
+
+template <typename T, int N, typename A>
+inline void local_vector<T, N, A>::push_back(T&& x) {
+    if (r_.last_ == r_.capacity_)
+        grow();
+    r_.construct(r_.last_, std::move(x));
+    ++r_.last_;
+}
+
+template <typename T, int N, typename A> template <typename... Args>
+inline void local_vector<T, N, A>::emplace_back(Args&&... args) {
+    if (r_.last_ == r_.capacity_)
+        grow();
+    r_.construct(r_.last_, std::forward<Args>(args)...);
+    ++r_.last_;
+}
+
+template <typename T, int N, typename A>
+inline void local_vector<T, N, A>::pop_back() {
+    assert(r_.first_ != r_.last_);
+    --r_.last_;
+    r_.destroy(r_.last_);
+}
+
+template <typename T, int N, typename A>
+inline void local_vector<T, N, A>::clear() {
+    for (auto it = r_.first_; it != r_.last_; ++it)
+        r_.destroy(it);
+    r_.last_ = r_.first_;
+}
+
+template <typename T, int N, typename A>
+inline void local_vector<T, N, A>::resize(size_type n, value_type v) {
+    if (capacity() < n)
+        grow(n);
+    auto it = r_.first_ + n;
+    auto xt = r_.last_;
+    r_.last_ = it;
+    for (; it < xt; ++it)
+        r_.destroy(it);
+    for (; xt < it; ++xt)
+        r_.construct(xt, v);
+}
+
+template <typename T, int N, typename A>
+local_vector<T, N, A>&
+local_vector<T, N, A>::operator=(const local_vector<T, N, A>& x) {
+    if (&x != this) {
+        clear();
+        if (capacity() < x.capacity())
+            grow(x.capacity());
+        for (auto xit = x.r_.first_; xit != x.r_.last_; ++xit, ++r_.last_)
+            r_.construct(r_.last_, *xit);
+    }
+    return *this;
+}
+
+template <typename T, int N, typename A> template <int NN, typename AA>
+local_vector<T, N, A>&
+local_vector<T, N, A>::operator=(const local_vector<T, NN, AA>& x) {
+    clear();
+    if (capacity() < x.capacity())
+        grow(x.capacity());
+    for (auto xit = x.r_.first_; xit != x.r_.last_; ++xit, ++r_.last_)
+        r_.construct(r_.last_, *xit);
+    return *this;
+}
+
+template <typename T, int N, typename A>
+inline T* local_vector<T, N, A>::erase(iterator position) {
+    return erase(position, position + 1);
+}
+
+template <typename T, int N, typename A>
+T* local_vector<T, N, A>::erase(iterator first, iterator last) {
+    if (first != last) {
+        iterator it = first, xend = end();
+        for (; last != xend; ++it, ++last)
+            *it = std::move(*last);
+        r_.last_ = it;
+        for (; it != xend; ++it)
+            r_.destroy(it);
+    }
+    return first;
+}
+
+#endif
diff --git a/silo/masstree/log.cc b/silo/masstree/log.cc
new file mode 100644 (file)
index 0000000..5cc15a7
--- /dev/null
@@ -0,0 +1,864 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "log.hh"
+#include "kvthread.hh"
+#include "kvrow.hh"
+#include "file.hh"
+#include "query_masstree.hh"
+#include "masstree_tcursor.hh"
+#include "masstree_insert.hh"
+#include "masstree_remove.hh"
+#include "misc.hh"
+#include "msgpack.hh"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+using lcdf::String;
+
+kvepoch_t global_log_epoch;
+kvepoch_t global_wake_epoch;
+struct timeval log_epoch_interval;
+static struct timeval log_epoch_time;
+extern Masstree::default_table* tree;
+
+kvepoch_t rec_ckp_min_epoch;
+kvepoch_t rec_ckp_max_epoch;
+logreplay::info_type *rec_log_infos;
+kvepoch_t rec_replay_min_epoch;
+kvepoch_t rec_replay_max_epoch;
+kvepoch_t rec_replay_min_quiescent_last_epoch;
+
+struct logrec_base {
+    uint32_t command_;
+    uint32_t size_;
+
+    static size_t size() {
+        return sizeof(logrec_base);
+    }
+    static size_t store(char *buf, uint32_t command) {
+        // XXX check alignment on some architectures
+        logrec_base *lr = reinterpret_cast<logrec_base *>(buf);
+        lr->command_ = command;
+        lr->size_ = sizeof(*lr);
+        return sizeof(*lr);
+    }
+    static bool check(const char *buf) {
+        const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
+        return lr->size_ >= sizeof(*lr);
+    }
+    static uint32_t command(const char *buf) {
+        const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
+        return lr->command_;
+    }
+};
+
+struct logrec_epoch {
+    uint32_t command_;
+    uint32_t size_;
+    kvepoch_t epoch_;
+
+    static size_t size() {
+        return sizeof(logrec_epoch);
+    }
+    static size_t store(char *buf, uint32_t command, kvepoch_t epoch) {
+        // XXX check alignment on some architectures
+        logrec_epoch *lr = reinterpret_cast<logrec_epoch *>(buf);
+        lr->command_ = command;
+        lr->size_ = sizeof(*lr);
+        lr->epoch_ = epoch;
+        return sizeof(*lr);
+    }
+    static bool check(const char *buf) {
+        const logrec_epoch *lr = reinterpret_cast<const logrec_epoch *>(buf);
+        return lr->size_ >= sizeof(*lr);
+    }
+};
+
+struct logrec_kv {
+    uint32_t command_;
+    uint32_t size_;
+    kvtimestamp_t ts_;
+    uint32_t keylen_;
+    char buf_[0];
+
+    static size_t size(uint32_t keylen, uint32_t vallen) {
+        return sizeof(logrec_kv) + keylen + vallen;
+    }
+    static size_t store(char *buf, uint32_t command,
+                        Str key, Str val,
+                        kvtimestamp_t ts) {
+        // XXX check alignment on some architectures
+        logrec_kv *lr = reinterpret_cast<logrec_kv *>(buf);
+        lr->command_ = command;
+        lr->size_ = sizeof(*lr) + key.len + val.len;
+        lr->ts_ = ts;
+        lr->keylen_ = key.len;
+        memcpy(lr->buf_, key.s, key.len);
+        memcpy(lr->buf_ + key.len, val.s, val.len);
+        return sizeof(*lr) + key.len + val.len;
+    }
+    static bool check(const char *buf) {
+        const logrec_kv *lr = reinterpret_cast<const logrec_kv *>(buf);
+        return lr->size_ >= sizeof(*lr)
+            && lr->size_ >= sizeof(*lr) + lr->keylen_;
+    }
+};
+
+struct logrec_kvdelta {
+    uint32_t command_;
+    uint32_t size_;
+    kvtimestamp_t ts_;
+    kvtimestamp_t prev_ts_;
+    uint32_t keylen_;
+    char buf_[0];
+
+    static size_t size(uint32_t keylen, uint32_t vallen) {
+        return sizeof(logrec_kvdelta) + keylen + vallen;
+    }
+    static size_t store(char *buf, uint32_t command,
+                        Str key, Str val,
+                        kvtimestamp_t prev_ts, kvtimestamp_t ts) {
+        // XXX check alignment on some architectures
+        logrec_kvdelta *lr = reinterpret_cast<logrec_kvdelta *>(buf);
+        lr->command_ = command;
+        lr->size_ = sizeof(*lr) + key.len + val.len;
+        lr->ts_ = ts;
+        lr->prev_ts_ = prev_ts;
+        lr->keylen_ = key.len;
+        memcpy(lr->buf_, key.s, key.len);
+        memcpy(lr->buf_ + key.len, val.s, val.len);
+        return sizeof(*lr) + key.len + val.len;
+    }
+    static bool check(const char *buf) {
+        const logrec_kvdelta *lr = reinterpret_cast<const logrec_kvdelta *>(buf);
+        return lr->size_ >= sizeof(*lr)
+            && lr->size_ >= sizeof(*lr) + lr->keylen_;
+    }
+};
+
+
+logset* logset::make(int size) {
+    static_assert(sizeof(loginfo) == 2 * CACHE_LINE_SIZE, "unexpected sizeof(loginfo)");
+    assert(size > 0 && size <= 64);
+    char* x = new char[sizeof(loginfo) * size + sizeof(loginfo::logset_info) + CACHE_LINE_SIZE];
+    char* ls_pos = x + sizeof(loginfo::logset_info);
+    uintptr_t left = reinterpret_cast<uintptr_t>(ls_pos) % CACHE_LINE_SIZE;
+    if (left)
+        ls_pos += CACHE_LINE_SIZE - left;
+    logset* ls = reinterpret_cast<logset*>(ls_pos);
+    ls->li_[-1].lsi_.size_ = size;
+    ls->li_[-1].lsi_.allocation_offset_ = (int) (x - ls_pos);
+    for (int i = 0; i != size; ++i)
+        new((void*) &ls->li_[i]) loginfo(ls, i);
+    return ls;
+}
+
+void logset::free(logset* ls) {
+    for (int i = 0; i != ls->size(); ++i)
+        ls->li_[i].~loginfo();
+    delete[] (reinterpret_cast<char*>(ls) + ls->li_[-1].lsi_.allocation_offset_);
+}
+
+
+loginfo::loginfo(logset* ls, int logindex) {
+    f_.lock_ = 0;
+    f_.waiting_ = 0;
+    f_.filename_ = String().internal_rep();
+    f_.filename_.ref();
+
+    len_ = 20 * 1024 * 1024;
+    pos_ = 0;
+    buf_ = (char *) malloc(len_);
+    always_assert(buf_);
+    log_epoch_ = 0;
+    quiescent_epoch_ = 0;
+    wake_epoch_ = 0;
+    flushed_epoch_ = 0;
+
+    ti_ = 0;
+    f_.logset_ = ls;
+    logindex_ = logindex;
+
+    (void) padding1_;
+}
+
+loginfo::~loginfo() {
+    f_.filename_.deref();
+    free(buf_);
+}
+
+void loginfo::initialize(const String& logfile) {
+    assert(!ti_);
+
+    f_.filename_.deref();
+    f_.filename_ = logfile.internal_rep();
+    f_.filename_.ref();
+
+    ti_ = threadinfo::make(threadinfo::TI_LOG, logindex_);
+    int r = ti_->run(logger_trampoline, this);
+    always_assert(r == 0);
+}
+
+// one logger thread per logs[].
+static void check_epoch() {
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    if (timercmp(&tv, &log_epoch_time, >)) {
+        log_epoch_time = tv;
+        timeradd(&log_epoch_time, &log_epoch_interval, &log_epoch_time);
+        global_log_epoch = global_log_epoch.next_nonzero(); // 0 isn't valid
+    }
+}
+
+void* loginfo::run() {
+    {
+        logreplay replayer(f_.filename_);
+        replayer.replay(ti_->index(), ti_);
+    }
+
+    int fd = open(String(f_.filename_).c_str(),
+                  O_WRONLY | O_APPEND | O_CREAT, 0666);
+    always_assert(fd >= 0);
+    char *x_buf = (char *) malloc(len_);
+    always_assert(x_buf);
+
+    while (1) {
+        uint32_t nb = 0;
+        acquire();
+        kvepoch_t ge = global_log_epoch, we = global_wake_epoch;
+        if (wake_epoch_ != we) {
+            wake_epoch_ = we;
+            quiescent_epoch_ = 0;
+        }
+        // If the writing threads appear quiescent, and aren't about to write
+        // to the log (f_.waiting_ != 0), then write a quiescence
+        // notification.
+        if (!recovering && pos_ == 0 && !quiescent_epoch_
+            && ge != log_epoch_ && ge != we && !f_.waiting_) {
+            quiescent_epoch_ = log_epoch_ = ge;
+            char *p = buf_;
+            p += logrec_epoch::store(p, logcmd_epoch, log_epoch_);
+            if (log_epoch_ == wake_epoch_)
+                p += logrec_base::store(p, logcmd_wake);
+            p += logrec_base::store(p, logcmd_quiesce);
+            pos_ = p - buf_;
+        }
+        if (!recovering && pos_ > 0) {
+            uint32_t x_pos = pos_;
+            std::swap(buf_, x_buf);
+            pos_ = 0;
+            kvepoch_t x_epoch = log_epoch_;
+            release();
+            ssize_t r = write(fd, x_buf, x_pos);
+            always_assert(r == ssize_t(x_pos));
+            fsync(fd);
+            flushed_epoch_ = x_epoch;
+            // printf("log %d %d\n", ti_->index(), x_pos);
+            nb = x_pos;
+        } else
+            release();
+        if (nb < len_ / 4)
+            napms(200);
+        if (ti_->index() == 0)
+            check_epoch();
+    }
+
+    return 0;
+}
+
+void* loginfo::logger_trampoline(threadinfo* ti) {
+    loginfo* li = static_cast<loginfo*>(ti->thread_data());
+    return li->run();
+}
+
+
+
+// log entry format: see log.hh
+void loginfo::record(int command, const query_times& qtimes,
+                     Str key, Str value) {
+    assert(!recovering);
+    size_t n = logrec_kvdelta::size(key.len, value.len)
+        + logrec_epoch::size() + logrec_base::size();
+    waitlist wait = { &wait };
+    int stalls = 0;
+    while (1) {
+        if (len_ - pos_ >= n
+            && (wait.next == &wait || f_.waiting_ == &wait)) {
+            kvepoch_t we = global_wake_epoch;
+
+            // Potentially record a new epoch.
+            if (qtimes.epoch != log_epoch_) {
+                log_epoch_ = qtimes.epoch;
+                pos_ += logrec_epoch::store(buf_ + pos_, logcmd_epoch, qtimes.epoch);
+            }
+
+            if (quiescent_epoch_) {
+                // We're recording a new log record on a log that's been
+                // quiescent for a while. If the quiescence marker has been
+                // flushed, then all epochs less than the query epoch are
+                // effectively on disk.
+                if (flushed_epoch_ == quiescent_epoch_)
+                    flushed_epoch_ = qtimes.epoch;
+                quiescent_epoch_ = 0;
+                while (we < qtimes.epoch)
+                    we = cmpxchg(&global_wake_epoch, we, qtimes.epoch);
+            }
+
+            // Log epochs should be recorded in monotonically increasing
+            // order, but the wake epoch may be ahead of the query epoch (if
+            // the query took a while). So potentially record an EARLIER
+            // wake_epoch. This will get fixed shortly by the next log
+            // record.
+            if (we != wake_epoch_ && qtimes.epoch < we)
+                we = qtimes.epoch;
+            if (we != wake_epoch_) {
+                wake_epoch_ = we;
+                pos_ += logrec_base::store(buf_ + pos_, logcmd_wake);
+            }
+
+            if (command == logcmd_put && qtimes.prev_ts
+                && !(qtimes.prev_ts & 1))
+                pos_ += logrec_kvdelta::store(buf_ + pos_,
+                                              logcmd_modify, key, value,
+                                              qtimes.prev_ts, qtimes.ts);
+            else
+                pos_ += logrec_kv::store(buf_ + pos_,
+                                         command, key, value, qtimes.ts);
+
+            if (f_.waiting_ == &wait)
+                f_.waiting_ = wait.next;
+            release();
+            return;
+        }
+
+        // Otherwise must spin
+        if (wait.next == &wait) {
+            waitlist** p = &f_.waiting_;
+            while (*p)
+                p = &(*p)->next;
+            *p = &wait;
+            wait.next = 0;
+        }
+        release();
+        if (stalls == 0)
+            printf("stall\n");
+        else if (stalls % 25 == 0)
+            printf("stall %d\n", stalls);
+        ++stalls;
+        napms(50);
+        acquire();
+    }
+}
+
+void loginfo::record(int command, const query_times& qtimes, Str key,
+                     const lcdf::Json* req, const lcdf::Json* end_req) {
+    lcdf::StringAccum sa(128);
+    msgpack::unparser<lcdf::StringAccum> cu(sa);
+    cu.write_array_header(end_req - req);
+    for (; req != end_req; ++req)
+        cu << *req;
+    record(command, qtimes, key, Str(sa.data(), sa.length()));
+}
+
+
+// replay
+
+logreplay::logreplay(const String &filename)
+    : filename_(filename), errno_(0), buf_()
+{
+    int fd = open(filename_.c_str(), O_RDONLY);
+    if (fd == -1) {
+    fail:
+        errno_ = errno;
+        buf_ = 0;
+        if (fd != -1)
+            (void) close(fd);
+        return;
+    }
+
+    struct stat sb;
+    int r = fstat(fd, &sb);
+    if (r == -1)
+        goto fail;
+
+    size_ = sb.st_size;
+    if (size_ != 0) {
+        // XXX what if filename_ is too big to mmap in its entirety?
+        // XXX should support mmaping/writing in pieces
+        buf_ = (char *) ::mmap(0, size_, PROT_READ, MAP_FILE | MAP_PRIVATE,
+                               fd, 0);
+        if (buf_ == MAP_FAILED)
+            goto fail;
+    }
+
+    (void) close(fd);
+}
+
+logreplay::~logreplay()
+{
+    unmap();
+}
+
+int
+logreplay::unmap()
+{
+    int r = 0;
+    if (buf_) {
+        r = munmap(buf_, size_);
+        buf_ = 0;
+    }
+    return r;
+}
+
+
+struct logrecord {
+    uint32_t command;
+    Str key;
+    Str val;
+    kvtimestamp_t ts;
+    kvtimestamp_t prev_ts;
+    kvepoch_t epoch;
+
+    const char *extract(const char *buf, const char *end);
+
+    template <typename T>
+    void run(T& table, std::vector<lcdf::Json>& jrepo, threadinfo& ti);
+
+  private:
+    inline void apply(row_type*& value, bool found,
+                      std::vector<lcdf::Json>& jrepo, threadinfo& ti);
+};
+
+const char *
+logrecord::extract(const char *buf, const char *end)
+{
+    const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
+    if (unlikely(size_t(end - buf) < sizeof(*lr)
+                 || lr->size_ < sizeof(*lr)
+                 || size_t(end - buf) < lr->size_
+                 || lr->command_ == logcmd_none)) {
+    fail:
+        command = logcmd_none;
+        return end;
+    }
+
+    command = lr->command_;
+    if (command == logcmd_put || command == logcmd_replace
+        || command == logcmd_remove) {
+        const logrec_kv *lk = reinterpret_cast<const logrec_kv *>(buf);
+        if (unlikely(lk->size_ < sizeof(*lk)
+                     || lk->keylen_ > MASSTREE_MAXKEYLEN
+                     || sizeof(*lk) + lk->keylen_ > lk->size_))
+            goto fail;
+        ts = lk->ts_;
+        key.assign(lk->buf_, lk->keylen_);
+        val.assign(lk->buf_ + lk->keylen_, lk->size_ - sizeof(*lk) - lk->keylen_);
+    } else if (command == logcmd_modify) {
+        const logrec_kvdelta *lk = reinterpret_cast<const logrec_kvdelta *>(buf);
+        if (unlikely(lk->keylen_ > MASSTREE_MAXKEYLEN
+                     || sizeof(*lk) + lk->keylen_ > lk->size_))
+            goto fail;
+        ts = lk->ts_;
+        prev_ts = lk->prev_ts_;
+        key.assign(lk->buf_, lk->keylen_);
+        val.assign(lk->buf_ + lk->keylen_, lk->size_ - sizeof(*lk) - lk->keylen_);
+    } else if (command == logcmd_epoch) {
+        const logrec_epoch *lre = reinterpret_cast<const logrec_epoch *>(buf);
+        if (unlikely(lre->size_ < logrec_epoch::size()))
+            goto fail;
+        epoch = lre->epoch_;
+    }
+
+    return buf + lr->size_;
+}
+
+template <typename T>
+void logrecord::run(T& table, std::vector<lcdf::Json>& jrepo, threadinfo& ti) {
+    row_marker m;
+    if (command == logcmd_remove) {
+        ts |= 1;
+        m.marker_type_ = row_marker::mt_remove;
+        val = Str((const char*) &m, sizeof(m));
+    }
+
+    typename T::cursor_type lp(table, key);
+    bool found = lp.find_insert(ti);
+    if (!found)
+        ti.advance_timestamp(lp.node_timestamp());
+    apply(lp.value(), found, jrepo, ti);
+    lp.finish(1, ti);
+}
+
+static lcdf::Json* parse_changeset(Str changeset,
+                                   std::vector<lcdf::Json>& jrepo) {
+    msgpack::parser mp(changeset.udata());
+    unsigned index = 0;
+    Str value;
+    size_t pos = 0;
+    while (mp.position() != changeset.end()) {
+        if (pos == jrepo.size())
+            jrepo.resize(pos + 2);
+        mp >> index >> value;
+        jrepo[pos] = index;
+        jrepo[pos + 1] = String::make_stable(value);
+        pos += 2;
+    }
+    return jrepo.data() + pos;
+}
+
+inline void logrecord::apply(row_type*& value, bool found,
+                             std::vector<lcdf::Json>& jrepo, threadinfo& ti) {
+    row_type** cur_value = &value;
+    if (!found)
+        *cur_value = 0;
+
+    // find point to insert change (may be after some delta markers)
+    while (*cur_value && row_is_delta_marker(*cur_value)
+           && (*cur_value)->timestamp() > ts)
+        cur_value = &row_get_delta_marker(*cur_value)->prev_;
+
+    // check out of date
+    if (*cur_value && (*cur_value)->timestamp() >= ts)
+        return;
+
+    // if not modifying, delete everything earlier
+    if (command != logcmd_modify)
+        while (row_type* old_value = *cur_value) {
+            if (row_is_delta_marker(old_value)) {
+                ti.mark(tc_replay_remove_delta);
+                *cur_value = row_get_delta_marker(old_value)->prev_;
+            } else
+                *cur_value = 0;
+            old_value->deallocate(ti);
+        }
+
+    // actually apply change
+    if (command == logcmd_replace)
+        *cur_value = row_type::create1(val, ts, ti);
+    else if (command != logcmd_modify
+             || (*cur_value && (*cur_value)->timestamp() == prev_ts)) {
+        lcdf::Json* end_req = parse_changeset(val, jrepo);
+        if (command != logcmd_modify)
+            *cur_value = row_type::create(jrepo.data(), end_req, ts, ti);
+        else {
+            row_type* old_value = *cur_value;
+            *cur_value = old_value->update(jrepo.data(), end_req, ts, ti);
+            if (*cur_value != old_value)
+                old_value->deallocate(ti);
+        }
+    } else {
+        // XXX assume that memory exists before saved request -- it does
+        // in conventional log replay, but that's an ugly interface
+        val.s -= sizeof(row_delta_marker<row_type>);
+        val.len += sizeof(row_delta_marker<row_type>);
+        row_type* new_value = row_type::create1(val, ts | 1, ti);
+        row_delta_marker<row_type>* dm = row_get_delta_marker(new_value, true);
+        dm->marker_type_ = row_marker::mt_delta;
+        dm->prev_ts_ = prev_ts;
+        dm->prev_ = *cur_value;
+        *cur_value = new_value;
+        ti.mark(tc_replay_create_delta);
+    }
+
+    // clean up
+    while (value && row_is_delta_marker(value)) {
+        row_type **prev = 0, **trav = &value;
+        while (*trav && row_is_delta_marker(*trav)) {
+            prev = trav;
+            trav = &row_get_delta_marker(*trav)->prev_;
+        }
+        if (prev && *trav
+            && row_get_delta_marker(*prev)->prev_ts_ == (*trav)->timestamp()) {
+            row_type *old_prev = *prev;
+            Str req = old_prev->col(0);
+            req.s += sizeof(row_delta_marker<row_type>);
+            req.len -= sizeof(row_delta_marker<row_type>);
+            const lcdf::Json* end_req = parse_changeset(req, jrepo);
+            *prev = (*trav)->update(jrepo.data(), end_req, old_prev->timestamp() - 1, ti);
+            if (*prev != *trav)
+                (*trav)->deallocate(ti);
+            old_prev->deallocate(ti);
+            ti.mark(tc_replay_remove_delta);
+        } else
+            break;
+    }
+}
+
+
+logreplay::info_type
+logreplay::info() const
+{
+    info_type x;
+    x.first_epoch = x.last_epoch = x.wake_epoch = x.min_post_quiescent_wake_epoch = 0;
+    x.quiescent = true;
+
+    const char *buf = buf_, *end = buf_ + size_;
+    off_t nr = 0;
+    bool log_corrupt = false;
+    while (buf + sizeof(logrec_base) <= end) {
+        const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
+        if (unlikely(lr->size_ < sizeof(logrec_base))) {
+            log_corrupt = true;
+            break;
+        } else if (unlikely(buf + lr->size_ > end))
+            break;
+        x.quiescent = lr->command_ == logcmd_quiesce;
+        if (lr->command_ == logcmd_epoch) {
+            const logrec_epoch *lre =
+                reinterpret_cast<const logrec_epoch *>(buf);
+            if (unlikely(lre->size_ < sizeof(*lre))) {
+                log_corrupt = true;
+                break;
+            }
+            if (!x.first_epoch)
+                x.first_epoch = lre->epoch_;
+            x.last_epoch = lre->epoch_;
+            if (x.wake_epoch && x.wake_epoch > x.last_epoch) // wrap-around
+                x.wake_epoch = 0;
+        } else if (lr->command_ == logcmd_wake)
+            x.wake_epoch = x.last_epoch;
+#if !NDEBUG
+        else if (lr->command_ != logcmd_put
+                 && lr->command_ != logcmd_replace
+                 && lr->command_ != logcmd_modify
+                 && lr->command_ != logcmd_remove
+                 && lr->command_ != logcmd_quiesce) {
+            log_corrupt = true;
+            break;
+        }
+#endif
+        buf += lr->size_;
+        ++nr;
+    }
+
+    fprintf(stderr, "replay %s: %" PRIdOFF_T " records, first %" PRIu64 ", last %" PRIu64 ", wake %" PRIu64 "%s%s @%zu\n",
+            filename_.c_str(), nr, x.first_epoch.value(),
+            x.last_epoch.value(), x.wake_epoch.value(),
+            x.quiescent ? ", quiescent" : "",
+            log_corrupt ? ", CORRUPT" : "", buf - buf_);
+    return x;
+}
+
+kvepoch_t
+logreplay::min_post_quiescent_wake_epoch(kvepoch_t quiescent_epoch) const
+{
+    kvepoch_t e = 0;
+    const char *buf = buf_, *end = buf_ + size_;
+    bool log_corrupt = false;
+    while (buf + sizeof(logrec_base) <= end) {
+        const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
+        if (unlikely(lr->size_ < sizeof(logrec_base))) {
+            log_corrupt = true;
+            break;
+        } else if (unlikely(buf + lr->size_ > end))
+            break;
+        if (lr->command_ == logcmd_epoch) {
+            const logrec_epoch *lre =
+                reinterpret_cast<const logrec_epoch *>(buf);
+            if (unlikely(lre->size_ < sizeof(*lre))) {
+                log_corrupt = true;
+                break;
+            }
+            e = lre->epoch_;
+        } else if (lr->command_ == logcmd_wake
+                   && e
+                   && e >= quiescent_epoch)
+            return e;
+        buf += lr->size_;
+    }
+    (void) log_corrupt;
+    return 0;
+}
+
+uint64_t
+logreplay::replayandclean1(kvepoch_t min_epoch, kvepoch_t max_epoch,
+                           threadinfo *ti)
+{
+    uint64_t nr = 0;
+    const char *pos = buf_, *end = buf_ + size_;
+    const char *repbegin = 0, *repend = 0;
+    logrecord lr;
+    std::vector<lcdf::Json> jrepo;
+
+    // XXX
+    while (pos < end) {
+        const char *nextpos = lr.extract(pos, end);
+        if (lr.command == logcmd_none) {
+            fprintf(stderr, "replay %s: %" PRIu64 " entries replayed, CORRUPT @%zu\n",
+                    filename_.c_str(), nr, pos - buf_);
+            break;
+        }
+        if (lr.command == logcmd_epoch) {
+            if ((min_epoch && lr.epoch < min_epoch)
+                || (!min_epoch && !repbegin))
+                repbegin = pos;
+            if (lr.epoch >= max_epoch) {
+                always_assert(repbegin);
+                repend = nextpos;
+                break;
+            }
+        }
+        if (!lr.epoch || (min_epoch && lr.epoch < min_epoch)) {
+            pos = nextpos;
+            if (repbegin)
+                repend = nextpos;
+            continue;
+        }
+        // replay only part of log after checkpoint
+        // could replay everything, the if() here tests
+        // correctness of checkpoint scheme.
+        assert(repbegin);
+        repend = nextpos;
+        if (lr.key.len) { // skip empty entry
+            if (lr.command == logcmd_put
+                || lr.command == logcmd_replace
+                || lr.command == logcmd_modify
+                || lr.command == logcmd_remove)
+                lr.run(tree->table(), jrepo, *ti);
+            ++nr;
+            if (nr % 100000 == 0)
+                fprintf(stderr,
+                        "replay %s: %" PRIu64 " entries replayed\n",
+                        filename_.c_str(), nr);
+        }
+        pos = nextpos;
+    }
+
+    // rewrite portion of log
+    if (!repbegin)
+        repbegin = repend = buf_;
+    else if (!repend) {
+        fprintf(stderr, "replay %s: surprise repend\n", filename_.c_str());
+        repend = pos;
+    }
+
+    char tmplog[256];
+    int r = snprintf(tmplog, sizeof(tmplog), "%s.tmp", filename_.c_str());
+    always_assert(r >= 0 && size_t(r) < sizeof(tmplog));
+
+    printf("replay %s: truncate from %" PRIdOFF_T " to %" PRIdSIZE_T " [%" PRIdSIZE_T ",%" PRIdSIZE_T ")\n",
+           filename_.c_str(), size_, repend - repbegin,
+           repbegin - buf_, repend - buf_);
+
+    bool need_copy = repbegin != buf_;
+    int fd;
+    if (!need_copy)
+        fd = replay_truncate(repend - repbegin);
+    else
+        fd = replay_copy(tmplog, repbegin, repend);
+
+    r = fsync(fd);
+    always_assert(r == 0);
+    r = close(fd);
+    always_assert(r == 0);
+
+    // replace old log with rewritten log
+    if (unmap() != 0)
+        abort();
+
+    if (need_copy) {
+        r = rename(tmplog, filename_.c_str());
+        if (r != 0) {
+            fprintf(stderr, "replay %s: %s\n", filename_.c_str(), strerror(errno));
+            abort();
+        }
+    }
+
+    return nr;
+}
+
+int
+logreplay::replay_truncate(size_t len)
+{
+    int fd = open(filename_.c_str(), O_RDWR);
+    if (fd < 0) {
+        fprintf(stderr, "replay %s: %s\n", filename_.c_str(), strerror(errno));
+        abort();
+    }
+
+    struct stat sb;
+    int r = fstat(fd, &sb);
+    if (r != 0) {
+        fprintf(stderr, "replay %s: %s\n", filename_.c_str(), strerror(errno));
+        abort();
+    } else if (sb.st_size < off_t(len)) {
+        fprintf(stderr, "replay %s: bad length %" PRIdOFF_T "\n", filename_.c_str(), sb.st_size);
+        abort();
+    }
+
+    r = ftruncate(fd, len);
+    if (r != 0) {
+        fprintf(stderr, "replay %s: truncate: %s\n", filename_.c_str(), strerror(errno));
+        abort();
+    }
+
+    off_t off = lseek(fd, len, SEEK_SET);
+    if (off == (off_t) -1) {
+        fprintf(stderr, "replay %s: seek: %s\n", filename_.c_str(), strerror(errno));
+        abort();
+    }
+
+    return fd;
+}
+
+int
+logreplay::replay_copy(const char *tmpname, const char *first, const char *last)
+{
+    int fd = creat(tmpname, 0666);
+    if (fd < 0) {
+        fprintf(stderr, "replay %s: create: %s\n", tmpname, strerror(errno));
+        abort();
+    }
+
+    ssize_t w = safe_write(fd, first, last - first);
+    always_assert(w >= 0 && w == last - first);
+
+    return fd;
+}
+
+void
+logreplay::replay(int which, threadinfo *ti)
+{
+    waituntilphase(REC_LOG_TS);
+    // find the maximum timestamp of entries in the log
+    if (buf_) {
+        info_type x = info();
+        pthread_mutex_lock(&rec_mu);
+        rec_log_infos[which] = x;
+        pthread_mutex_unlock(&rec_mu);
+    }
+    inactive();
+
+    waituntilphase(REC_LOG_ANALYZE_WAKE);
+    if (buf_) {
+        if (rec_replay_min_quiescent_last_epoch
+            && rec_replay_min_quiescent_last_epoch <= rec_log_infos[which].wake_epoch)
+            rec_log_infos[which].min_post_quiescent_wake_epoch =
+                min_post_quiescent_wake_epoch(rec_replay_min_quiescent_last_epoch);
+    }
+    inactive();
+
+    waituntilphase(REC_LOG_REPLAY);
+    if (buf_) {
+        ti->rcu_start();
+        uint64_t nr = replayandclean1(rec_replay_min_epoch, rec_replay_max_epoch, ti);
+        ti->rcu_stop();
+        printf("recovered %" PRIu64 " records from %s\n", nr, filename_.c_str());
+    }
+    inactive();
+}
diff --git a/silo/masstree/log.hh b/silo/masstree/log.hh
new file mode 100644 (file)
index 0000000..449e04f
--- /dev/null
@@ -0,0 +1,233 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_LOG_HH
+#define MASSTREE_LOG_HH
+#include "kvthread.hh"
+#include "string.hh"
+#include "kvproto.hh"
+#include "str.hh"
+#include <pthread.h>
+class logset;
+using lcdf::Str;
+namespace lcdf { class Json; }
+
+// in-memory log.
+// more than one, to reduce contention on the lock.
+class loginfo {
+  public:
+    void initialize(const lcdf::String& logfile);
+
+    inline void acquire();
+    inline void release();
+
+    inline kvepoch_t flushed_epoch() const;
+    inline bool quiescent() const;
+
+    // logging
+    struct query_times {
+        kvepoch_t epoch;
+        kvtimestamp_t ts;
+        kvtimestamp_t prev_ts;
+    };
+    // NB may block!
+    void record(int command, const query_times& qt, Str key, Str value);
+    void record(int command, const query_times& qt, Str key,
+                const lcdf::Json* req, const lcdf::Json* end_req);
+
+  private:
+    struct waitlist {
+        waitlist* next;
+    };
+    struct front {
+        uint32_t lock_;
+        waitlist* waiting_;
+        lcdf::String::rep_type filename_;
+        logset* logset_;
+    };
+    struct logset_info {
+        int32_t size_;
+        int allocation_offset_;
+    };
+
+    front f_;
+    char padding1_[CACHE_LINE_SIZE - sizeof(front)];
+
+    kvepoch_t log_epoch_;       // epoch written to log (non-quiescent)
+    kvepoch_t quiescent_epoch_; // epoch we went quiescent
+    kvepoch_t wake_epoch_;      // epoch for which we recorded a wake command
+    kvepoch_t flushed_epoch_;   // epoch fsync()ed to disk
+
+    union {
+        struct {
+            char *buf_;
+            uint32_t pos_;
+            uint32_t len_;
+
+            // We have logged all writes up to, but not including,
+            // flushed_epoch_.
+            // Log is quiesced to disk if quiescent_epoch_ != 0
+            // and quiescent_epoch_ == flushed_epoch_.
+            // When a log wakes up from quiescence, it sets global_wake_epoch;
+            // other threads must record a logcmd_wake in their logs.
+            // Invariant: log_epoch_ != quiescent_epoch_ (unless both are 0).
+
+            threadinfo *ti_;
+            int logindex_;
+        };
+        struct {
+            char cache_line_2_[CACHE_LINE_SIZE - 4 * sizeof(kvepoch_t) - sizeof(logset_info)];
+            logset_info lsi_;
+        };
+    };
+
+    loginfo(logset* ls, int logindex);
+    ~loginfo();
+    void* run();
+    static void* logger_trampoline(threadinfo* ti);
+
+    friend class logset;
+};
+
+class logset {
+  public:
+    static logset* make(int size);
+    static void free(logset* ls);
+
+    inline int size() const;
+    inline loginfo& log(int i);
+    inline const loginfo& log(int i) const;
+
+  private:
+    loginfo li_[0];
+};
+
+extern kvepoch_t global_log_epoch;
+extern kvepoch_t global_wake_epoch;
+extern struct timeval log_epoch_interval;
+
+enum logcommand {
+    logcmd_none = 0,
+    logcmd_put = 0x5455506B,            // "kPUT" in little endian
+    logcmd_replace = 0x3155506B,        // "kPU1"
+    logcmd_modify = 0x444F4D6B,         // "kMOD"
+    logcmd_remove = 0x4D45526B,         // "kREM"
+    logcmd_epoch = 0x4F50456B,          // "kEPO"
+    logcmd_quiesce = 0x4955516B,        // "kQUI"
+    logcmd_wake = 0x4B41576B            // "kWAK"
+};
+
+
+class logreplay {
+  public:
+    logreplay(const lcdf::String &filename);
+    ~logreplay();
+    int unmap();
+
+    struct info_type {
+        kvepoch_t first_epoch;
+        kvepoch_t last_epoch;
+        kvepoch_t wake_epoch;
+        kvepoch_t min_post_quiescent_wake_epoch;
+        bool quiescent;
+    };
+    info_type info() const;
+    kvepoch_t min_post_quiescent_wake_epoch(kvepoch_t quiescent_epoch) const;
+
+    void replay(int i, threadinfo *ti);
+
+  private:
+    lcdf::String filename_;
+    int errno_;
+    off_t size_;
+    char *buf_;
+
+    uint64_t replayandclean1(kvepoch_t min_epoch, kvepoch_t max_epoch,
+                             threadinfo *ti);
+    int replay_truncate(size_t len);
+    int replay_copy(const char *tmpname, const char *first, const char *last);
+};
+
+enum { REC_NONE, REC_CKP, REC_LOG_TS, REC_LOG_ANALYZE_WAKE,
+       REC_LOG_REPLAY, REC_DONE };
+extern void recphase(int nactive, int state);
+extern void waituntilphase(int phase);
+extern void inactive();
+extern pthread_mutex_t rec_mu;
+extern logreplay::info_type *rec_log_infos;
+extern kvepoch_t rec_ckp_min_epoch;
+extern kvepoch_t rec_ckp_max_epoch;
+extern kvepoch_t rec_replay_min_epoch;
+extern kvepoch_t rec_replay_max_epoch;
+extern kvepoch_t rec_replay_min_quiescent_last_epoch;
+
+
+inline void loginfo::acquire() {
+    test_and_set_acquire(&f_.lock_);
+}
+
+inline void loginfo::release() {
+    test_and_set_release(&f_.lock_);
+}
+
+inline kvepoch_t loginfo::flushed_epoch() const {
+    return flushed_epoch_;
+}
+
+inline bool loginfo::quiescent() const {
+    return quiescent_epoch_ && quiescent_epoch_ == flushed_epoch_;
+}
+
+inline int logset::size() const {
+    return li_[-1].lsi_.size_;
+}
+
+inline loginfo& logset::log(int i) {
+    assert(unsigned(i) < unsigned(size()));
+    return li_[i];
+}
+
+inline const loginfo& logset::log(int i) const {
+    assert(unsigned(i) < unsigned(size()));
+    return li_[i];
+}
+
+
+template <typename R>
+struct row_delta_marker : public row_marker {
+    kvtimestamp_t prev_ts_;
+    R *prev_;
+    char s_[0];
+};
+
+template <typename R>
+inline bool row_is_delta_marker(const R* row) {
+    if (row_is_marker(row)) {
+        const row_marker* m =
+            reinterpret_cast<const row_marker *>(row->col(0).s);
+        return m->marker_type_ == m->mt_delta;
+    } else
+        return false;
+}
+
+template <typename R>
+inline row_delta_marker<R>* row_get_delta_marker(const R* row, bool force = false) {
+    (void) force;
+    assert(force || row_is_delta_marker(row));
+    return reinterpret_cast<row_delta_marker<R>*>
+        (const_cast<char*>(row->col(0).s));
+}
+
+#endif
diff --git a/silo/masstree/masstree.hh b/silo/masstree/masstree.hh
new file mode 100644 (file)
index 0000000..34452ee
--- /dev/null
@@ -0,0 +1,97 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_HH
+#define MASSTREE_HH
+#include "compiler.hh"
+#include "str.hh"
+#include "ksearch.hh"
+
+namespace Masstree {
+using lcdf::Str;
+using lcdf::String;
+
+template <typename T> class value_print;
+
+template <int LW = 15, int IW = LW> struct nodeparams {
+    static constexpr int leaf_width = LW;
+    static constexpr int internode_width = IW;
+    static constexpr bool concurrent = true;
+    static constexpr bool prefetch = true;
+    static constexpr int bound_method = bound_method_binary;
+    static constexpr int debug_level = 0;
+    static constexpr bool printable_keys = true;
+    typedef uint64_t ikey_type;
+};
+
+template <int LW, int IW> constexpr int nodeparams<LW, IW>::leaf_width;
+template <int LW, int IW> constexpr int nodeparams<LW, IW>::internode_width;
+template <int LW, int IW> constexpr int nodeparams<LW, IW>::debug_level;
+
+template <typename P> class node_base;
+template <typename P> class leaf;
+template <typename P> class internode;
+template <typename P> class leafvalue;
+template <typename P> class key;
+template <typename P> class basic_table;
+template <typename P> class unlocked_tcursor;
+template <typename P> class tcursor;
+
+template <typename P>
+class basic_table {
+  public:
+    typedef P param_type;
+    typedef node_base<P> node_type;
+    typedef leaf<P> leaf_type;
+    typedef typename P::value_type value_type;
+    typedef typename P::threadinfo_type threadinfo;
+    typedef unlocked_tcursor<P> unlocked_cursor_type;
+    typedef tcursor<P> cursor_type;
+
+    inline basic_table();
+
+    void initialize(threadinfo& ti);
+    void destroy(threadinfo& ti);
+
+    inline node_type* root() const;
+    inline node_type* fix_root();
+
+    bool get(Str key, value_type& value, threadinfo& ti) const;
+
+    template <typename F>
+    int scan(Str firstkey, bool matchfirst, F& scanner, threadinfo& ti) const;
+    template <typename F>
+    int rscan(Str firstkey, bool matchfirst, F& scanner, threadinfo& ti) const;
+
+    template <typename F>
+    inline int modify(Str key, F& f, threadinfo& ti);
+    template <typename F>
+    inline int modify_insert(Str key, F& f, threadinfo& ti);
+
+    inline void print(FILE* f = 0, int indent = 0) const;
+
+  private:
+    node_type* root_;
+
+    template <typename H, typename F>
+    int scan(H helper, Str firstkey, bool matchfirst,
+             F& scanner, threadinfo& ti) const;
+
+    friend class unlocked_tcursor<P>;
+    friend class tcursor<P>;
+};
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/masstree_get.hh b/silo/masstree/masstree_get.hh
new file mode 100644 (file)
index 0000000..d91fcf8
--- /dev/null
@@ -0,0 +1,120 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_GET_HH
+#define MASSTREE_GET_HH 1
+#include "masstree_tcursor.hh"
+#include "masstree_key.hh"
+namespace Masstree {
+
+template <typename P>
+bool unlocked_tcursor<P>::find_unlocked(threadinfo& ti)
+{
+    int match;
+    key_indexed_position kx;
+    node_base<P>* root = const_cast<node_base<P>*>(root_);
+
+ retry:
+    n_ = root->reach_leaf(ka_, v_, ti);
+
+ forward:
+    if (v_.deleted())
+        goto retry;
+
+    n_->prefetch();
+    perm_ = n_->permutation();
+    kx = leaf<P>::bound_type::lower(ka_, *this);
+    if (kx.p >= 0) {
+        lv_ = n_->lv_[kx.p];
+        lv_.prefetch(n_->keylenx_[kx.p]);
+        match = n_->ksuf_matches(kx.p, ka_);
+    } else
+        match = 0;
+    if (n_->has_changed(v_)) {
+        ti.mark(threadcounter(tc_stable_leaf_insert + n_->simple_has_split(v_)));
+        n_ = n_->advance_to_key(ka_, v_, ti);
+        goto forward;
+    }
+
+    if (match < 0) {
+        ka_.shift_by(-match);
+        root = lv_.layer();
+        goto retry;
+    } else
+        return match;
+}
+
+template <typename P>
+inline bool basic_table<P>::get(Str key, value_type &value,
+                                threadinfo& ti) const
+{
+    unlocked_tcursor<P> lp(*this, key);
+    bool found = lp.find_unlocked(ti);
+    if (found)
+        value = lp.value();
+    return found;
+}
+
+template <typename P>
+bool tcursor<P>::find_locked(threadinfo& ti)
+{
+    node_base<P>* root = const_cast<node_base<P>*>(root_);
+    nodeversion_type v;
+    permuter_type perm;
+
+ retry:
+    n_ = root->reach_leaf(ka_, v, ti);
+
+ forward:
+    if (v.deleted())
+        goto retry;
+
+    n_->prefetch();
+    perm = n_->permutation();
+    fence();
+    kx_ = leaf<P>::bound_type::lower(ka_, *n_);
+    if (kx_.p >= 0) {
+        leafvalue<P> lv = n_->lv_[kx_.p];
+        lv.prefetch(n_->keylenx_[kx_.p]);
+        state_ = n_->ksuf_matches(kx_.p, ka_);
+        if (state_ < 0 && !n_->has_changed(v) && !lv.layer()->has_split()) {
+            ka_.shift_by(-state_);
+            root = lv.layer();
+            goto retry;
+        }
+    } else
+        state_ = 0;
+
+    n_->lock(v, ti.lock_fence(tc_leaf_lock));
+    if (n_->has_changed(v) || n_->permutation() != perm) {
+        ti.mark(threadcounter(tc_stable_leaf_insert + n_->simple_has_split(v)));
+        n_->unlock();
+        n_ = n_->advance_to_key(ka_, v, ti);
+        goto forward;
+    } else if (unlikely(state_ < 0)) {
+        ka_.shift_by(-state_);
+        n_->lv_[kx_.p] = root = n_->lv_[kx_.p].layer()->unsplit_ancestor();
+        n_->unlock();
+        goto retry;
+    } else if (unlikely(n_->deleted_layer())) {
+        ka_.unshift_all();
+        root = const_cast<node_base<P>*>(root_);
+        goto retry;
+    }
+    return state_;
+}
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/masstree_insert.hh b/silo/masstree/masstree_insert.hh
new file mode 100644 (file)
index 0000000..8ed40da
--- /dev/null
@@ -0,0 +1,181 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_INSERT_HH
+#define MASSTREE_INSERT_HH
+#include "masstree_get.hh"
+#include "masstree_split.hh"
+namespace Masstree {
+
+template <typename P>
+bool tcursor<P>::find_insert(threadinfo& ti)
+{
+    find_locked(ti);
+    original_n_ = n_;
+    original_v_ = n_->full_unlocked_version_value();
+
+    // maybe we found it
+    if (state_)
+        return true;
+
+    // otherwise mark as inserted but not present
+    state_ = 2;
+
+    // maybe we need a new layer
+    if (kx_.p >= 0)
+        return make_new_layer(ti);
+
+    // mark insertion if we are changing modification state
+    if (unlikely(n_->modstate_ != leaf<P>::modstate_insert)) {
+        masstree_invariant(n_->modstate_ == leaf<P>::modstate_remove);
+        n_->mark_insert();
+        n_->modstate_ = leaf<P>::modstate_insert;
+    }
+
+    // try inserting into this node
+    if (n_->size() < n_->width) {
+        kx_.p = permuter_type(n_->permutation_).back();
+        // don't inappropriately reuse position 0, which holds the ikey_bound
+        if (likely(kx_.p != 0) || !n_->prev_ || n_->ikey_bound() == ka_.ikey()) {
+            n_->assign(kx_.p, ka_, ti);
+            return false;
+        }
+    }
+
+    // otherwise must split
+    return make_split(ti);
+}
+
+template <typename P>
+bool tcursor<P>::make_new_layer(threadinfo& ti) {
+    key_type oka(n_->ksuf(kx_.p));
+    ka_.shift();
+    int kcmp = oka.compare(ka_);
+
+    // Create a twig of nodes until the suffixes diverge
+    leaf_type* twig_head = n_;
+    leaf_type* twig_tail = n_;
+    while (kcmp == 0) {
+        leaf_type* nl = leaf_type::make_root(0, twig_tail, ti);
+        nl->assign_initialize_for_layer(0, oka);
+        if (twig_head != n_)
+            twig_tail->lv_[0] = nl;
+        else
+            twig_head = nl;
+        nl->permutation_ = permuter_type::make_sorted(1);
+        twig_tail = nl;
+        new_nodes_.emplace_back(nl, nl->full_unlocked_version_value());
+        oka.shift();
+        ka_.shift();
+        kcmp = oka.compare(ka_);
+    }
+
+    // Estimate how much space will be required for keysuffixes
+    size_t ksufsize;
+    if (ka_.has_suffix() || oka.has_suffix())
+        ksufsize = (std::max(0, ka_.suffix_length())
+                    + std::max(0, oka.suffix_length())) * (n_->width / 2)
+            + n_->iksuf_[0].overhead(n_->width);
+    else
+        ksufsize = 0;
+    leaf_type *nl = leaf_type::make_root(ksufsize, twig_tail, ti);
+    nl->assign_initialize(0, kcmp < 0 ? oka : ka_, ti);
+    nl->assign_initialize(1, kcmp < 0 ? ka_ : oka, ti);
+    nl->lv_[kcmp > 0] = n_->lv_[kx_.p];
+    nl->lock(*nl, ti.lock_fence(tc_leaf_lock));
+    if (kcmp < 0)
+        nl->permutation_ = permuter_type::make_sorted(1);
+    else {
+        permuter_type permnl = permuter_type::make_sorted(2);
+        permnl.remove_to_back(0);
+        nl->permutation_ = permnl.value();
+    }
+    // In a prior version, recursive tree levels and true values were
+    // differentiated by a bit in the leafvalue. But this constrains the
+    // values users could assign for true values. So now we use bits in
+    // the key length, and changing a leafvalue from true value to
+    // recursive tree requires two writes. How to make this work in the
+    // face of concurrent lockless readers? Mark insertion so they
+    // retry.
+    n_->mark_insert();
+    fence();
+    if (twig_tail != n_)
+        twig_tail->lv_[0] = nl;
+    if (twig_head != n_)
+        n_->lv_[kx_.p] = twig_head;
+    else
+        n_->lv_[kx_.p] = nl;
+    n_->keylenx_[kx_.p] = n_->layer_keylenx;
+    updated_v_ = n_->full_unlocked_version_value();
+    n_->unlock();
+    n_ = nl;
+    kx_.i = kx_.p = kcmp < 0;
+    return false;
+}
+
+template <typename P>
+void tcursor<P>::finish_insert()
+{
+    permuter_type perm(n_->permutation_);
+    masstree_invariant(perm.back() == kx_.p);
+    perm.insert_from_back(kx_.i);
+    fence();
+    n_->permutation_ = perm.value();
+}
+
+template <typename P>
+inline void tcursor<P>::finish(int state, threadinfo& ti)
+{
+    if (state < 0 && state_ == 1) {
+        if (finish_remove(ti))
+            return;
+    } else if (state > 0 && state_ == 2)
+        finish_insert();
+    // we finally know this!
+    if (n_ == original_n_)
+        updated_v_ = n_->full_unlocked_version_value();
+    else
+        new_nodes_.emplace_back(n_, n_->full_unlocked_version_value());
+    n_->unlock();
+}
+
+template <typename P> template <typename F>
+inline int basic_table<P>::modify(Str key, F& f, threadinfo& ti)
+{
+    tcursor<P> lp(*this, key);
+    bool found = lp.find_locked(ti);
+    int answer;
+    if (found)
+        answer = f(key, true, lp.value(), ti, lp.node_timestamp());
+    else
+        answer = 0;
+    lp.finish(answer, ti);
+    return answer;
+}
+
+template <typename P> template <typename F>
+inline int basic_table<P>::modify_insert(Str key, F& f, threadinfo& ti)
+{
+    tcursor<P> lp(*this, key);
+    bool found = lp.find_insert(ti);
+    if (!found)
+        ti.advance_timestamp(lp.node_timestamp());
+    int answer = f(key, found, lp.value(), ti, lp.node_timestamp());
+    lp.finish(answer, ti);
+    return answer;
+}
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/masstree_key.hh b/silo/masstree/masstree_key.hh
new file mode 100644 (file)
index 0000000..4d1e8a7
--- /dev/null
@@ -0,0 +1,240 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_KEY_HH
+#define MASSTREE_KEY_HH
+#include "masstree.hh"
+#include "string_slice.hh"
+
+namespace Masstree {
+
+/** @brief Strings used as Masstree keys.
+
+    Masstree key strings are divided into parts: an initial slice, called
+    the <em>ikey</em>, and a suffix. The ikey is stored as a byte-swapped
+    integer, which is faster to compare than a string.
+
+    Keys can be <em>shifted</em> by the shift() method. <code>k.shift()</code>
+    is like <code>k = key(k.suffix())</code>: the key is reconstructed from
+    the original key's suffix. This corresponds to descending a layer in
+    Masstree. However, <code>k.shift()</code> is faster than the assignment,
+    and its effects can be undone by the <code>k.unshift_all()</code>
+    method. */
+template <typename I>
+class key {
+  public:
+    static constexpr int nikey = 1;
+    /** @brief Type of ikeys. */
+    typedef I ikey_type;
+    /** @brief Size of ikeys in bytes. */
+    static constexpr int ikey_size = sizeof(ikey_type);
+
+    /** @brief Construct an uninitialized key. */
+    key() {
+    }
+    /** @brief Construct a key for string @a s. */
+    key(Str s)
+        : ikey0_(string_slice<ikey_type>::make_comparable(s.s, s.len)),
+          len_(s.len), s_(s.s), first_(s.s) {
+    }
+    /** @brief Construct a key for string @a s with length @a len.
+        @pre @a len >= 0 */
+    key(const char* s, int len)
+        : ikey0_(string_slice<ikey_type>::make_comparable(s, len)),
+          len_(len), s_(s), first_(s) {
+    }
+    /** @brief Construct a key for ikey @a ikey.
+
+        Any trailing zero bytes in @a ikey are not counted towards the key's
+        length. */
+    explicit key(ikey_type ikey)
+        : ikey0_(ikey),
+          len_(ikey ? ikey_size - ctz(ikey) / 8 : 0), s_(0), first_(0) {
+    }
+    /** @brief Construct a key for ikey @a ikey with length @a len.
+        @pre @a len >= 0
+        @post length() >= 0 && length() <= ikey_size */
+    key(ikey_type ikey, int len)
+        : ikey0_(ikey),
+          len_(std::min(len, ikey_size)), s_(0), first_(0) {
+    }
+    /** @brief Construct a key with ikey @a ikey and suffix @a suf. */
+    key(ikey_type ikey, Str suf)
+        : ikey0_(ikey),
+          len_(ikey_size + suf.len), s_(suf.s - ikey_size), first_(s_) {
+    }
+
+    /** @brief Test if this key is empty (holds the empty string). */
+    bool empty() const {
+        return ikey0_ == 0 && len_ == 0;
+    }
+    /** @brief Return the ikey. */
+    ikey_type ikey() const {
+        return ikey0_;
+    }
+    /** @brief Return the key's length. */
+    int length() const {
+        return len_;
+    }
+    /** @brief Test whether this key has a suffix (length() > ikey_size). */
+    bool has_suffix() const {
+        return len_ > ikey_size;
+    }
+    /** @brief Return this key's suffix.
+        @pre has_suffix() */
+    Str suffix() const {
+        return Str(s_ + ikey_size, len_ - ikey_size);
+    }
+    /** @brief Return the length of this key's suffix.
+        @pre has_suffix() */
+    int suffix_length() const {
+        return len_ - ikey_size;
+    }
+
+    /** @brief Shift this key forward to model the current key's suffix.
+        @pre has_suffix() */
+    void shift() {
+        s_ += ikey_size;
+        len_ -= ikey_size;
+        ikey0_ = string_slice<ikey_type>::make_comparable_sloppy(s_, len_);
+    }
+    /** @brief Shift this key forward to model the current key's suffix.
+        @pre has_suffix() */
+    void shift_by(int delta) {
+        s_ += delta;
+        len_ -= delta;
+        ikey0_ = string_slice<ikey_type>::make_comparable_sloppy(s_, len_);
+    }
+    /** @brief Test whether this key has been shifted by shift(). */
+    bool is_shifted() const {
+        return first_ != s_;
+    }
+    /** @brief Undo all previous shift() calls. */
+    void unshift_all() {
+        if (s_ != first_) {
+            len_ += s_ - first_;
+            s_ = first_;
+            ikey0_ = string_slice<ikey_type>::make_comparable(s_, len_);
+        }
+    }
+
+    int compare(ikey_type ikey, int keylenx) const {
+        int cmp = ::compare(this->ikey(), ikey);
+        if (cmp == 0) {
+            int al = this->length();
+            if (al > ikey_size)
+                cmp = keylenx <= ikey_size;
+            else
+                cmp = al - keylenx;
+        }
+        return cmp;
+    }
+    int compare(const key<I>& x) const {
+        return compare(x.ikey(), x.length());
+    }
+
+    int unparse(char* data, int datalen) const {
+        int cplen = std::min(len_, datalen);
+        string_slice<ikey_type>::unparse_comparable(data, cplen, ikey0_, ikey_size);
+        if (cplen > ikey_size)
+            memcpy(data + ikey_size, s_ + ikey_size, cplen - ikey_size);
+        return cplen;
+    }
+    String unparse() const {
+        String s = String::make_uninitialized(len_);
+        unparse(s.mutable_data(), s.length());
+        return s;
+    }
+    int unparse_printable(char* data, int datalen) const {
+        String s = unparse().printable();
+        int cplen = std::min(s.length(), datalen);
+        memcpy(data, s.data(), cplen);
+        return cplen;
+    }
+    static String unparse_ikey(ikey_type ikey) {
+        key<ikey_type> k(ikey);
+        return k.unparse();
+    }
+
+    // used during scan
+    Str prefix_string() const {
+        return Str(first_, s_);
+    }
+    int prefix_length() const {
+        return s_ - first_;
+    }
+    Str full_string() const {
+        return Str(first_, s_ + len_);
+    }
+    operator Str() const {
+        return full_string();
+    }
+    bool increment() {
+        // Return true iff wrapped.
+        if (has_suffix()) {
+            ++ikey0_;
+            len_ = 1;
+            return unlikely(!ikey0_);
+        } else {
+            ++len_;
+            return false;
+        }
+    }
+    void assign_store_ikey(ikey_type ikey) {
+        ikey0_ = ikey;
+        *reinterpret_cast<ikey_type*>(const_cast<char*>(s_)) = host_to_net_order(ikey);
+    }
+    int assign_store_suffix(Str s) {
+        memcpy(const_cast<char*>(s_ + ikey_size), s.s, s.len);
+        return ikey_size + s.len;
+    }
+    void assign_store_length(int len) {
+        len_ = len;
+    }
+    void unshift() {
+        masstree_precondition(is_shifted());
+        s_ -= ikey_size;
+        ikey0_ = string_slice<ikey_type>::make_comparable_sloppy(s_, ikey_size);
+        len_ = ikey_size + 1;
+    }
+    void shift_clear() {
+        ikey0_ = 0;
+        len_ = 0;
+        s_ += ikey_size;
+    }
+    void shift_clear_reverse() {
+        ikey0_ = ~ikey_type(0);
+        len_ = ikey_size + 1;
+        s_ += ikey_size;
+    }
+
+  private:
+    ikey_type ikey0_;
+    int len_;
+    const char* s_;
+    const char* first_;
+};
+
+template <typename I> constexpr int key<I>::ikey_size;
+
+} // namespace Masstree
+
+template <typename I>
+inline std::ostream& operator<<(std::ostream& stream,
+                                const Masstree::key<I>& x) {
+    return stream << x.unparse();
+}
+
+#endif
diff --git a/silo/masstree/masstree_print.hh b/silo/masstree/masstree_print.hh
new file mode 100644 (file)
index 0000000..58c1666
--- /dev/null
@@ -0,0 +1,160 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_PRINT_HH
+#define MASSTREE_PRINT_HH
+#include "masstree_struct.hh"
+#include <stdio.h>
+
+namespace Masstree {
+
+template <typename T>
+class value_print {
+  public:
+    static void print(T value, FILE* f, const char* prefix,
+                      int indent, Str key, kvtimestamp_t initial_timestamp,
+                      char* suffix) {
+        value->print(f, prefix, indent, key, initial_timestamp, suffix);
+    }
+};
+
+template <>
+class value_print<unsigned char*> {
+  public:
+    static void print(unsigned char* value, FILE* f, const char* prefix,
+                      int indent, Str key, kvtimestamp_t,
+                      char* suffix) {
+        fprintf(f, "%s%*s%.*s = %p%s\n",
+                prefix, indent, "", key.len, key.s, value, suffix);
+    }
+};
+
+template <typename P>
+void node_base<P>::print(FILE *f, const char *prefix, int indent, int kdepth)
+{
+    if (this->isleaf())
+        ((leaf<P> *) this)->print(f, prefix, indent, kdepth);
+    else
+        ((internode<P> *) this)->print(f, prefix, indent, kdepth);
+}
+
+template <typename P>
+void leaf<P>::print(FILE *f, const char *prefix, int indent, int kdepth)
+{
+    f = f ? f : stderr;
+    prefix = prefix ? prefix : "";
+    typename node_base<P>::nodeversion_type v;
+    permuter_type perm;
+    do {
+        v = *this;
+        fence();
+        perm = permutation_;
+    } while (this->has_changed(v));
+
+    static const char* modstates[] = {"", "-", "D"};
+    char keybuf[MASSTREE_MAXKEYLEN];
+    fprintf(f, "%s%*sleaf %p: %d %s, version %x%s, permutation %s, ",
+            prefix, indent, "", this,
+            perm.size(), perm.size() == 1 ? "key" : "keys",
+            v.version_value(),
+            modstate_ <= 2 ? modstates[modstate_] : "??",
+            perm.unparse().c_str());
+    fprintf(f, "parent %p, prev %p, next %p ", parent_, prev_, next_.ptr);
+    if (ksuf_ && extrasize64_ < -1)
+        fprintf(f, "[ksuf i%dx%d] ", -extrasize64_ - 1, (int) ksuf_->capacity() / 64);
+    else if (ksuf_)
+        fprintf(f, "[ksuf x%d] ", (int) ksuf_->capacity() / 64);
+    else if (extrasize64_)
+        fprintf(f, "[ksuf i%d] ", extrasize64_);
+    if (P::debug_level > 0) {
+        kvtimestamp_t cts = timestamp_sub(created_at_[0], initial_timestamp);
+        fprintf(f, "@" PRIKVTSPARTS, KVTS_HIGHPART(cts), KVTS_LOWPART(cts));
+    }
+    fputc('\n', f);
+
+    if (v.deleted() || (perm[0] != 0 && prev_))
+        fprintf(f, "%s%*s%s = [] #0\n", prefix, indent + 2, "", key_type(ikey_bound()).unparse().c_str());
+
+    char xbuf[15];
+    for (int idx = 0; idx < perm.size(); ++idx) {
+        int p = perm[idx], l;
+        if (P::printable_keys)
+            l = this->get_key(p).unparse_printable(keybuf, sizeof(keybuf));
+        else
+            l = this->get_key(p).unparse(keybuf, sizeof(keybuf));
+        sprintf(xbuf, " #%x/%d", p, keylenx_[p]);
+        leafvalue_type lv = lv_[p];
+        if (this->has_changed(v)) {
+            fprintf(f, "%s%*s[NODE CHANGED]\n", prefix, indent + 2, "");
+            break;
+        } else if (!lv)
+            fprintf(f, "%s%*s%.*s = []%s\n", prefix, indent + 2, "", l, keybuf, xbuf);
+        else if (is_layer(p)) {
+            fprintf(f, "%s%*s%.*s = SUBTREE%s\n", prefix, indent + 2, "", l, keybuf, xbuf);
+            node_base<P> *n = lv.layer()->unsplit_ancestor();
+            n->print(f, prefix, indent + 4, kdepth + key_type::ikey_size);
+        } else {
+            typename P::value_type tvx = lv.value();
+            P::value_print_type::print(tvx, f, prefix, indent + 2, Str(keybuf, l), initial_timestamp, xbuf);
+        }
+    }
+
+    if (v.deleted())
+        fprintf(f, "%s%*s[DELETED]\n", prefix, indent + 2, "");
+}
+
+template <typename P>
+void internode<P>::print(FILE *f, const char *prefix, int indent, int kdepth)
+{
+    f = f ? f : stderr;
+    prefix = prefix ? prefix : "";
+    internode<P> copy(*this);
+    for (int i = 0; i < 100 && (copy.has_changed(*this) || this->inserting() || this->splitting()); ++i)
+        memcpy(&copy, this, sizeof(copy));
+
+    char keybuf[MASSTREE_MAXKEYLEN];
+    fprintf(f, "%s%*sinternode %p%s: %d keys, version %x, parent %p",
+            prefix, indent, "", this, this->deleted() ? " [DELETED]" : "",
+            copy.size(), copy.version_value(), copy.parent_);
+    if (P::debug_level > 0) {
+        kvtimestamp_t cts = timestamp_sub(created_at_[0], initial_timestamp);
+        fprintf(f, " @" PRIKVTSPARTS, KVTS_HIGHPART(cts), KVTS_LOWPART(cts));
+    }
+    fputc('\n', f);
+    for (int p = 0; p < copy.size(); ++p) {
+        if (copy.child_[p])
+            copy.child_[p]->print(f, prefix, indent + 4, kdepth);
+        else
+            fprintf(f, "%s%*s[]\n", prefix, indent + 4, "");
+        int l;
+        if (P::printable_keys)
+            l = copy.get_key(p).unparse_printable(keybuf, sizeof(keybuf));
+        else
+            l = copy.get_key(p).unparse(keybuf, sizeof(keybuf));
+        fprintf(f, "%s%*s%.*s\n", prefix, indent + 2, "", l, keybuf);
+    }
+    if (copy.child_[copy.size()])
+        copy.child_[copy.size()]->print(f, prefix, indent + 4, kdepth);
+    else
+        fprintf(f, "%s%*s[]\n", prefix, indent + 4, "");
+}
+
+template <typename P>
+void basic_table<P>::print(FILE *f, int indent) const {
+    root_->print(f ? f : stdout, "", indent, 0);
+}
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/masstree_remove.hh b/silo/masstree/masstree_remove.hh
new file mode 100644 (file)
index 0000000..a5e4ea8
--- /dev/null
@@ -0,0 +1,357 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_REMOVE_HH
+#define MASSTREE_REMOVE_HH
+#include "masstree_get.hh"
+#include "btree_leaflink.hh"
+#include "circular_int.hh"
+namespace Masstree {
+
+template <typename P>
+bool tcursor<P>::gc_layer(threadinfo& ti)
+{
+    find_locked(ti);
+    masstree_precondition(!n_->deleted() && !n_->deleted_layer());
+
+    // find_locked might return early if another gc_layer attempt has
+    // succeeded at removing multiple tree layers. So check that the whole
+    // key has been consumed
+    if (ka_.has_suffix())
+        return false;
+
+    // find the slot for the child tree
+    // ka_ is a multiple of ikey_size bytes long. We are looking for the entry
+    // for the next tree layer, which has keylenx_ corresponding to ikey_size+1.
+    // So if has_value(), then we found an entry for the same ikey, but with
+    // length ikey_size; we need to adjust ki_.
+    kx_.i += has_value();
+    if (kx_.i >= n_->size())
+        return false;
+    permuter_type perm(n_->permutation_);
+    kx_.p = perm[kx_.i];
+    if (n_->ikey0_[kx_.p] != ka_.ikey() || !n_->is_layer(kx_.p))
+        return false;
+
+    // remove redundant internode layers
+    node_type *layer;
+    while (1) {
+        layer = n_->lv_[kx_.p].layer();
+        if (layer->has_split())
+            n_->lv_[kx_.p] = layer = layer->unsplit_ancestor();
+        if (layer->isleaf())
+            break;
+
+        internode_type *in = static_cast<internode_type *>(layer);
+        if (in->size() > 0 && !in->has_split())
+            return false;
+        in->lock(*in, ti.lock_fence(tc_internode_lock));
+        if (in->has_split() && !in->has_parent())
+            in->mark_root();
+        if (in->size() > 0 || in->has_split()) {
+            in->unlock();
+            return false;
+        }
+
+        node_type *child = in->child_[0];
+        child->set_parent(node_type::parent_for_layer_root(n_));
+        n_->lv_[kx_.p] = child;
+        in->mark_split();
+        in->set_parent(child);  // ensure concurrent reader finds true root
+                                // NB: now p->parent() might weirdly be a LEAF!
+        in->unlock();
+        in->deallocate_rcu(ti);
+    }
+
+    // we are left with a leaf child
+    leaf_type *lf = static_cast<leaf_type *>(layer);
+    if (lf->size() > 0 && !lf->has_split())
+        return false;
+    lf->lock(*lf, ti.lock_fence(tc_leaf_lock));
+    if (lf->has_split() && !lf->has_parent())
+        lf->mark_root();
+    if (lf->size() > 0 || lf->has_split()) {
+        lf->unlock();
+        return false;
+    }
+
+    // child is an empty leaf: kill it
+    masstree_invariant(!lf->prev_ && !lf->next_.ptr);
+    masstree_invariant(!lf->deleted());
+    masstree_invariant(!lf->deleted_layer());
+    if (circular_int<kvtimestamp_t>::less(n_->node_ts_, lf->node_ts_))
+        n_->node_ts_ = lf->node_ts_;
+    lf->mark_deleted_layer();   // NB DO NOT mark as deleted (see above)
+    lf->unlock();
+    lf->deallocate_rcu(ti);
+    return true;
+}
+
+template <typename P>
+struct gc_layer_rcu_callback : public P::threadinfo_type::rcu_callback {
+    typedef typename P::threadinfo_type threadinfo;
+    node_base<P>* root_;
+    int len_;
+    char s_[0];
+    gc_layer_rcu_callback(node_base<P>* root, Str prefix)
+        : root_(root), len_(prefix.length()) {
+        memcpy(s_, prefix.data(), len_);
+    }
+    void operator()(threadinfo& ti);
+    size_t size() const {
+        return len_ + sizeof(*this);
+    }
+    static void make(node_base<P>* root, Str prefix, threadinfo& ti);
+};
+
+template <typename P>
+void gc_layer_rcu_callback<P>::operator()(threadinfo& ti)
+{
+    root_ = root_->unsplit_ancestor();
+    if (!root_->deleted()) {    // if not destroying tree...
+        tcursor<P> lp(root_, s_, len_);
+        bool do_remove = lp.gc_layer(ti);
+        if (!do_remove || !lp.finish_remove(ti))
+            lp.n_->unlock();
+        ti.deallocate(this, size(), memtag_masstree_gc);
+    }
+}
+
+template <typename P>
+void gc_layer_rcu_callback<P>::make(node_base<P>* root, Str prefix,
+                                    threadinfo& ti)
+{
+    size_t sz = prefix.len + sizeof(gc_layer_rcu_callback<P>);
+    void *data = ti.allocate(sz, memtag_masstree_gc);
+    gc_layer_rcu_callback<P> *cb =
+        new(data) gc_layer_rcu_callback<P>(root, prefix);
+    ti.rcu_register(cb);
+}
+
+template <typename P>
+bool tcursor<P>::finish_remove(threadinfo& ti)
+{
+    if (n_->modstate_ == leaf<P>::modstate_insert) {
+        n_->mark_insert();
+        n_->modstate_ = leaf<P>::modstate_remove;
+    }
+
+    permuter_type perm(n_->permutation_);
+    perm.remove(kx_.i);
+    n_->permutation_ = perm.value();
+    if (perm.size())
+        return false;
+    else
+        return remove_leaf(n_, root_, ka_.prefix_string(), ti);
+}
+
+template <typename P>
+bool tcursor<P>::remove_leaf(leaf_type* leaf, node_type* root,
+                             Str prefix, threadinfo& ti)
+{
+    if (!leaf->prev_) {
+        if (!leaf->next_.ptr && !prefix.empty())
+            gc_layer_rcu_callback<P>::make(root, prefix, ti);
+        return false;
+    }
+
+    // mark leaf deleted, RCU-free
+    leaf->mark_deleted();
+    leaf->deallocate_rcu(ti);
+
+    // Ensure node that becomes responsible for our keys has its node_ts_ kept
+    // up to date
+    while (1) {
+        leaf_type *prev = leaf->prev_;
+        kvtimestamp_t prev_ts = prev->node_ts_;
+        while (circular_int<kvtimestamp_t>::less(prev_ts, leaf->node_ts_)
+               && !bool_cmpxchg(&prev->node_ts_, prev_ts, leaf->node_ts_))
+            prev_ts = prev->node_ts_;
+        fence();
+        if (prev == leaf->prev_)
+            break;
+    }
+
+    // Unlink leaf from doubly-linked leaf list
+    btree_leaflink<leaf_type>::unlink(leaf);
+
+    // Remove leaf from tree. This is simple unless the leaf is the first
+    // child of its parent, in which case we need to traverse up until we find
+    // its key.
+    node_type *n = leaf;
+    ikey_type ikey = leaf->ikey_bound(), reshape_ikey = 0;
+    bool reshaping = false;
+
+    while (1) {
+        internode_type *p = n->locked_parent(ti);
+        masstree_invariant(p);
+        n->unlock();
+
+        int kp = internode_type::bound_type::upper(ikey, *p);
+        masstree_invariant(kp == 0 || p->compare_key(ikey, kp - 1) == 0);
+
+        if (kp > 0) {
+            p->mark_insert();
+            if (!reshaping) {
+                p->shift_down(kp - 1, kp, p->nkeys_ - kp);
+                --p->nkeys_;
+            } else
+                p->ikey0_[kp - 1] = reshape_ikey;
+            if (kp > 1 || p->child_[0]) {
+                if (p->size() == 0)
+                    collapse(p, ikey, root, prefix, ti);
+                else
+                    p->unlock();
+                break;
+            }
+        }
+
+        if (!reshaping) {
+            if (p->size() == 0) {
+                p->mark_deleted();
+                p->deallocate_rcu(ti);
+            } else {
+                reshaping = true;
+                reshape_ikey = p->ikey0_[0];
+                p->child_[0] = 0;
+            }
+        }
+
+        n = p;
+    }
+
+    return true;
+}
+
+template <typename P>
+void tcursor<P>::collapse(internode_type* p, ikey_type ikey,
+                          node_type* root, Str prefix, threadinfo& ti)
+{
+    masstree_precondition(p && p->locked());
+
+    while (1) {
+        internode_type *gp = p->locked_parent(ti);
+        if (!internode_type::parent_exists(gp)) {
+            if (!prefix.empty())
+                gc_layer_rcu_callback<P>::make(root, prefix, ti);
+            p->unlock();
+            break;
+        }
+
+        int kp = key_upper_bound(ikey, *gp);
+        masstree_invariant(gp->child_[kp] == p);
+        gp->child_[kp] = p->child_[0];
+        p->child_[0]->set_parent(gp);
+
+        p->mark_deleted();
+        p->unlock();
+        p->deallocate_rcu(ti);
+
+        p = gp;
+        if (p->size() != 0) {
+            p->unlock();
+            break;
+        }
+    }
+}
+
+template <typename P>
+struct destroy_rcu_callback : public P::threadinfo_type::rcu_callback {
+    typedef typename P::threadinfo_type threadinfo;
+    typedef typename node_base<P>::leaf_type leaf_type;
+    typedef typename node_base<P>::internode_type internode_type;
+    node_base<P>* root_;
+    int count_;
+    destroy_rcu_callback(node_base<P>* root)
+        : root_(root), count_(0) {
+    }
+    void operator()(threadinfo& ti);
+    static void make(node_base<P>* root, Str prefix, threadinfo& ti);
+  private:
+    static inline node_base<P>** link_ptr(node_base<P>* n);
+    static inline void enqueue(node_base<P>* n, node_base<P>**& tailp);
+};
+
+template <typename P>
+inline node_base<P>** destroy_rcu_callback<P>::link_ptr(node_base<P>* n) {
+    if (n->isleaf())
+        return &static_cast<leaf_type*>(n)->parent_;
+    else
+        return &static_cast<internode_type*>(n)->parent_;
+}
+
+template <typename P>
+inline void destroy_rcu_callback<P>::enqueue(node_base<P>* n,
+                                             node_base<P>**& tailp) {
+    *tailp = n;
+    tailp = link_ptr(n);
+}
+
+template <typename P>
+void destroy_rcu_callback<P>::operator()(threadinfo& ti) {
+    if (++count_ == 1) {
+        root_ = root_->unsplit_ancestor();
+        root_->lock();
+        root_->mark_deleted_tree(); // i.e., deleted but not splitting
+        root_->unlock();
+        ti.rcu_register(this);
+        return;
+    }
+
+    node_base<P>* workq;
+    node_base<P>** tailp = &workq;
+    enqueue(root_, tailp);
+
+    while (node_base<P>* n = workq) {
+        node_base<P>** linkp = link_ptr(n);
+        if (linkp != tailp)
+            workq = *linkp;
+        else {
+            workq = 0;
+            tailp = &workq;
+        }
+
+        if (n->isleaf()) {
+            leaf_type* l = static_cast<leaf_type*>(n);
+            typename leaf_type::permuter_type perm = l->permutation();
+            for (int i = 0; i != l->size(); ++i) {
+                int p = perm[i];
+                if (l->is_layer(p))
+                    enqueue(l->lv_[p].layer(), tailp);
+            }
+            l->deallocate(ti);
+        } else {
+            internode_type* in = static_cast<internode_type*>(n);
+            for (int i = 0; i != in->size() + 1; ++i)
+                if (in->child_[i])
+                    enqueue(in->child_[i], tailp);
+            in->deallocate(ti);
+        }
+    }
+    ti.deallocate(this, sizeof(this), memtag_masstree_gc);
+}
+
+template <typename P>
+void basic_table<P>::destroy(threadinfo& ti) {
+    if (root_) {
+        void* data = ti.allocate(sizeof(destroy_rcu_callback<P>), memtag_masstree_gc);
+        destroy_rcu_callback<P>* cb = new(data) destroy_rcu_callback<P>(root_);
+        ti.rcu_register(cb);
+        root_ = 0;
+    }
+}
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/masstree_scan.hh b/silo/masstree/masstree_scan.hh
new file mode 100644 (file)
index 0000000..0497d48
--- /dev/null
@@ -0,0 +1,399 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_SCAN_HH
+#define MASSTREE_SCAN_HH
+#include "masstree_tcursor.hh"
+#include "masstree_struct.hh"
+namespace Masstree {
+
+template <typename P>
+class scanstackelt {
+  public:
+    typedef leaf<P> leaf_type;
+    typedef typename leaf_type::leafvalue_type leafvalue_type;
+    typedef typename leaf_type::bound_type bound_type;
+    typedef typename P::ikey_type ikey_type;
+    typedef key<ikey_type> key_type;
+    typedef typename leaf_type::permuter_type permuter_type;
+    typedef typename P::threadinfo_type threadinfo;
+    typedef typename node_base<P>::nodeversion_type nodeversion_type;
+
+    leaf<P>* node() const {
+        return n_;
+    }
+    typename nodeversion_type::value_type full_version_value() const {
+        return (v_.version_value() << permuter_type::size_bits) + perm_.size();
+    }
+    int size() const {
+        return perm_.size();
+    }
+    permuter_type permutation() const {
+        return perm_;
+    }
+    int operator()(const key_type &k, const scanstackelt<P> &n, int p) {
+        return n.n_->compare_key(k, p);
+    }
+
+  private:
+    node_base<P> *root_;
+    leaf<P> *n_;
+    nodeversion_type v_;
+    permuter_type perm_;
+    int ki_;
+
+    enum { scan_emit, scan_find_next, scan_down, scan_up, scan_retry };
+
+    scanstackelt() {
+    }
+
+    template <typename H>
+    int find_initial(H& helper, key_type& ka, bool emit_equal,
+                     leafvalue_type& entry, threadinfo& ti);
+    template <typename H>
+    int find_retry(H& helper, key_type& ka, threadinfo& ti);
+    template <typename H>
+    int find_next(H& helper, key_type& ka, leafvalue_type& entry);
+
+    int kp() const {
+        if (unsigned(ki_) < unsigned(perm_.size()))
+            return perm_[ki_];
+        else
+            return -1;
+    }
+
+    template <typename PX> friend class basic_table;
+};
+
+struct forward_scan_helper {
+    bool initial_ksuf_match(int ksuf_compare, bool emit_equal) const {
+        return ksuf_compare > 0 || (ksuf_compare == 0 && emit_equal);
+    }
+    template <typename K> bool is_duplicate(const K &k,
+                                            typename K::ikey_type ikey,
+                                            int keylenx) const {
+        return k.compare(ikey, keylenx) >= 0;
+    }
+    template <typename K, typename N> int lower(const K &k, const N *n) const {
+        return N::bound_type::lower_by(k, *n, *n).i;
+    }
+    template <typename K, typename N>
+    int lower_with_position(const K &k, const N *n, int &kp) const {
+        key_indexed_position kx = N::bound_type::lower_by(k, *n, *n);
+        kp = kx.p;
+        return kx.i;
+    }
+    void found() const {
+    }
+    int next(int ki) const {
+        return ki + 1;
+    }
+    template <typename N, typename K>
+    N *advance(const N *n, const K &) const {
+        return n->safe_next();
+    }
+    template <typename N, typename K>
+    typename N::nodeversion_type stable(const N *n, const K &) const {
+        return n->stable();
+    }
+    template <typename K> void shift_clear(K &ka) const {
+        ka.shift_clear();
+    }
+};
+
+struct reverse_scan_helper {
+    // We run ki backwards, referring to perm.size() each time through,
+    // because inserting elements into a node need not bump its version.
+    // Therefore, if we decremented ki, starting from a node's original
+    // size(), we might miss some concurrently inserted keys!
+    // Also, a node's size might change DURING a lower_bound operation.
+    // The "backwards" ki must be calculated using the size taken by the
+    // lower_bound, NOT some later size() (which might be bigger or smaller).
+    // The helper type reverse_scan_node allows this.
+    reverse_scan_helper()
+        : upper_bound_(false) {
+    }
+    bool initial_ksuf_match(int ksuf_compare, bool emit_equal) const {
+        return ksuf_compare < 0 || (ksuf_compare == 0 && emit_equal);
+    }
+    template <typename K> bool is_duplicate(const K &k,
+                                            typename K::ikey_type ikey,
+                                            int keylenx) const {
+        return k.compare(ikey, keylenx) <= 0 && !upper_bound_;
+    }
+    template <typename K, typename N> int lower(const K &k, const N *n) const {
+        if (upper_bound_)
+            return n->size() - 1;
+        key_indexed_position kx = N::bound_type::lower_by(k, *n, *n);
+        return kx.i - (kx.p < 0);
+    }
+    template <typename K, typename N>
+    int lower_with_position(const K &k, const N *n, int &kp) const {
+        key_indexed_position kx = N::bound_type::lower_by(k, *n, *n);
+        kp = kx.p;
+        return kx.i - (kx.p < 0);
+    }
+    int next(int ki) const {
+        return ki - 1;
+    }
+    void found() const {
+        upper_bound_ = false;
+    }
+    template <typename N, typename K>
+    N *advance(const N *n, K &k) const {
+        k.assign_store_ikey(n->ikey_bound());
+        k.assign_store_length(0);
+        return n->prev_;
+    }
+    template <typename N, typename K>
+    typename N::nodeversion_type stable(N *&n, const K &k) const {
+        while (1) {
+            typename N::nodeversion_type v = n->stable();
+            N *next = n->safe_next();
+            int cmp;
+            if (!next
+                || (cmp = ::compare(k.ikey(), next->ikey_bound())) < 0
+                || (cmp == 0 && k.length() == 0))
+                return v;
+            n = next;
+        }
+    }
+    template <typename K> void shift_clear(K &ka) const {
+        ka.shift_clear_reverse();
+        upper_bound_ = true;
+    }
+  private:
+    mutable bool upper_bound_;
+};
+
+
+template <typename P> template <typename H>
+int scanstackelt<P>::find_initial(H& helper, key_type& ka, bool emit_equal,
+                                  leafvalue_type& entry, threadinfo& ti)
+{
+    int kp, keylenx = 0;
+    char suffixbuf[MASSTREE_MAXKEYLEN];
+    Str suffix;
+
+ retry_root:
+    n_ = root_->reach_leaf(ka, v_, ti);
+
+ retry_node:
+    if (v_.deleted())
+        goto retry_root;
+    n_->prefetch();
+    perm_ = n_->permutation();
+
+    ki_ = helper.lower_with_position(ka, this, kp);
+    if (kp >= 0) {
+        keylenx = n_->keylenx_[kp];
+        fence();
+        entry = n_->lv_[kp];
+        entry.prefetch(keylenx);
+        if (n_->keylenx_has_ksuf(keylenx)) {
+            suffix = n_->ksuf(kp);
+            memcpy(suffixbuf, suffix.s, suffix.len);
+            suffix.s = suffixbuf;
+        }
+    }
+    if (n_->has_changed(v_)) {
+        ti.mark(tc_leaf_retry);
+        n_ = n_->advance_to_key(ka, v_, ti);
+        goto retry_node;
+    }
+
+    if (kp >= 0) {
+        if (n_->keylenx_is_layer(keylenx)) {
+            this[1].root_ = entry.layer();
+            return scan_down;
+        } else if (n_->keylenx_has_ksuf(keylenx)) {
+            int ksuf_compare = suffix.compare(ka.suffix());
+            if (helper.initial_ksuf_match(ksuf_compare, emit_equal)) {
+                int keylen = ka.assign_store_suffix(suffix);
+                ka.assign_store_length(keylen);
+                return scan_emit;
+            }
+        } else if (emit_equal)
+            return scan_emit;
+        // otherwise, this entry must be skipped
+        ki_ = helper.next(ki_);
+    }
+
+    return scan_find_next;
+}
+
+template <typename P> template <typename H>
+int scanstackelt<P>::find_retry(H& helper, key_type& ka, threadinfo& ti)
+{
+ retry:
+    n_ = root_->reach_leaf(ka, v_, ti);
+    if (v_.deleted())
+        goto retry;
+
+    n_->prefetch();
+    perm_ = n_->permutation();
+    ki_ = helper.lower(ka, this);
+    return scan_find_next;
+}
+
+template <typename P> template <typename H>
+int scanstackelt<P>::find_next(H &helper, key_type &ka, leafvalue_type &entry)
+{
+    int kp;
+
+    if (v_.deleted())
+        return scan_retry;
+
+ retry_entry:
+    kp = this->kp();
+    if (kp >= 0) {
+        ikey_type ikey = n_->ikey0_[kp];
+        int keylenx = n_->keylenx_[kp];
+        int keylen = keylenx;
+        fence();
+        entry = n_->lv_[kp];
+        entry.prefetch(keylenx);
+        if (n_->keylenx_has_ksuf(keylenx))
+            keylen = ka.assign_store_suffix(n_->ksuf(kp));
+
+        if (n_->has_changed(v_))
+            goto changed;
+        else if (helper.is_duplicate(ka, ikey, keylenx)) {
+            ki_ = helper.next(ki_);
+            goto retry_entry;
+        }
+
+        // We know we can emit the data collected above.
+        ka.assign_store_ikey(ikey);
+        helper.found();
+        if (n_->keylenx_is_layer(keylenx)) {
+            this[1].root_ = entry.layer();
+            return scan_down;
+        } else {
+            ka.assign_store_length(keylen);
+            return scan_emit;
+        }
+    }
+
+    if (!n_->has_changed(v_)) {
+        n_ = helper.advance(n_, ka);
+        if (!n_)
+            return scan_up;
+        n_->prefetch();
+    }
+
+ changed:
+    v_ = helper.stable(n_, ka);
+    perm_ = n_->permutation();
+    ki_ = helper.lower(ka, this);
+    return scan_find_next;
+}
+
+template <typename P> template <typename H, typename F>
+int basic_table<P>::scan(H helper,
+                         Str firstkey, bool emit_firstkey,
+                         F& scanner,
+                         threadinfo& ti) const
+{
+    typedef typename P::ikey_type ikey_type;
+    typedef typename node_type::key_type key_type;
+    typedef typename node_type::leaf_type::leafvalue_type leafvalue_type;
+    union {
+        ikey_type x[(MASSTREE_MAXKEYLEN + sizeof(ikey_type) - 1)/sizeof(ikey_type)];
+        char s[MASSTREE_MAXKEYLEN];
+    } keybuf;
+    masstree_precondition(firstkey.len <= (int) sizeof(keybuf));
+    memcpy(keybuf.s, firstkey.s, firstkey.len);
+    key_type ka(keybuf.s, firstkey.len);
+
+    typedef scanstackelt<param_type> mystack_type;
+    mystack_type stack[(MASSTREE_MAXKEYLEN + sizeof(ikey_type) - 1) / sizeof(ikey_type)];
+    int stackpos = 0;
+    stack[0].root_ = root_;
+    leafvalue_type entry = leafvalue_type::make_empty();
+
+    int scancount = 0;
+    int state;
+
+    while (1) {
+        state = stack[stackpos].find_initial(helper, ka, emit_firstkey,
+                                             entry, ti);
+        scanner.visit_leaf(stack[stackpos], ka, ti);
+        if (state != mystack_type::scan_down)
+            break;
+        ka.shift();
+        ++stackpos;
+    }
+
+    while (1) {
+        switch (state) {
+        case mystack_type::scan_emit:
+            ++scancount;
+            if (!scanner.visit_value(ka, entry.value(), ti))
+                goto done;
+            stack[stackpos].ki_ = helper.next(stack[stackpos].ki_);
+            state = stack[stackpos].find_next(helper, ka, entry);
+            break;
+
+        case mystack_type::scan_find_next:
+        find_next:
+            state = stack[stackpos].find_next(helper, ka, entry);
+            if (state != mystack_type::scan_up)
+                scanner.visit_leaf(stack[stackpos], ka, ti);
+            break;
+
+        case mystack_type::scan_up:
+            do {
+                if (--stackpos < 0)
+                    goto done;
+                ka.unshift();
+                stack[stackpos].ki_ = helper.next(stack[stackpos].ki_);
+            } while (unlikely(ka.empty()));
+            goto find_next;
+
+        case mystack_type::scan_down:
+            helper.shift_clear(ka);
+            ++stackpos;
+            goto retry;
+
+        case mystack_type::scan_retry:
+        retry:
+            state = stack[stackpos].find_retry(helper, ka, ti);
+            break;
+        }
+    }
+
+ done:
+    return scancount;
+}
+
+template <typename P> template <typename F>
+int basic_table<P>::scan(Str firstkey, bool emit_firstkey,
+                         F& scanner,
+                         threadinfo& ti) const
+{
+    return scan(forward_scan_helper(), firstkey, emit_firstkey, scanner, ti);
+}
+
+template <typename P> template <typename F>
+int basic_table<P>::rscan(Str firstkey, bool emit_firstkey,
+                          F& scanner,
+                          threadinfo& ti) const
+{
+    return scan(reverse_scan_helper(), firstkey, emit_firstkey, scanner, ti);
+}
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/masstree_split.hh b/silo/masstree/masstree_split.hh
new file mode 100644 (file)
index 0000000..6dbcfca
--- /dev/null
@@ -0,0 +1,274 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_SPLIT_HH
+#define MASSTREE_SPLIT_HH 1
+#include "masstree_tcursor.hh"
+#include "btree_leaflink.hh"
+namespace Masstree {
+
+/** @brief Return ikey at position @a i, assuming insert of @a ka at @a ka_i. */
+template <typename P>
+inline typename P::ikey_type
+leaf<P>::ikey_after_insert(const permuter_type& perm, int i,
+                           const key_type& ka, int ka_i) const
+{
+    if (i < ka_i)
+        return this->ikey0_[perm[i]];
+    else if (i == ka_i)
+        return ka.ikey();
+    else
+        return this->ikey0_[perm[i - 1]];
+}
+
+/** @brief Split this node into *@a nr and insert @a ka at position @a p.
+    @pre *@a nr is a new empty leaf
+    @pre this->locked() && @a nr->locked()
+    @post split_ikey is the first key in *@a nr
+    @return split type
+
+    If @a p == this->size() and *this is the rightmost node in the layer,
+    then this code assumes we're inserting nodes in sequential order, and
+    the split does not move any keys.
+
+    The split type is 0 if @a ka went into *this, 1 if the @a ka went into
+    *@a nr, and 2 for the sequential-order optimization (@a ka went into *@a
+    nr and no other keys were moved). */
+template <typename P>
+int leaf<P>::split_into(leaf<P>* nr, int p, const key_type& ka,
+                        ikey_type& split_ikey, threadinfo& ti)
+{
+    // B+tree leaf insertion.
+    // Split *this, with items [0,T::width), into *this + nr, simultaneously
+    // inserting "ka:value" at position "p" (0 <= p <= T::width).
+
+    // Let mid = floor(T::width / 2) + 1. After the split,
+    // "*this" contains [0,mid) and "nr" contains [mid,T::width+1).
+    // If p < mid, then x goes into *this, and the first element of nr
+    //   will be former item (mid - 1).
+    // If p >= mid, then x goes into nr.
+    masstree_precondition(!this->concurrent || (this->locked() && nr->locked()));
+    masstree_precondition(this->size() >= this->width - 1);
+
+    int width = this->size();   // == this->width or this->width - 1
+    int mid = this->width / 2 + 1;
+    if (p == 0 && !this->prev_)
+        mid = 1;
+    else if (p == width && !this->next_.ptr)
+        mid = width;
+
+    // Never separate keys with the same ikey0.
+    permuter_type perml(this->permutation_);
+    ikey_type mid_ikey = ikey_after_insert(perml, mid, ka, p);
+    if (mid_ikey == ikey_after_insert(perml, mid - 1, ka, p)) {
+        int midl = mid - 2, midr = mid + 1;
+        while (1) {
+            if (midr <= width
+                && mid_ikey != ikey_after_insert(perml, midr, ka, p)) {
+                mid = midr;
+                break;
+            } else if (midl >= 0
+                       && mid_ikey != ikey_after_insert(perml, midl, ka, p)) {
+                mid = midl + 1;
+                break;
+            }
+            --midl, ++midr;
+        }
+        masstree_invariant(mid > 0 && mid <= width);
+    }
+
+    typename permuter_type::value_type pv = perml.value_from(mid - (p < mid));
+    for (int x = mid; x <= width; ++x)
+        if (x == p)
+            nr->assign_initialize(x - mid, ka, ti);
+        else {
+            nr->assign_initialize(x - mid, this, pv & 15, ti);
+            pv >>= 4;
+        }
+    permuter_type permr = permuter_type::make_sorted(width + 1 - mid);
+    if (p >= mid)
+        permr.remove_to_back(p - mid);
+    nr->permutation_ = permr.value();
+
+    btree_leaflink<leaf<P>, P::concurrent>::link_split(this, nr);
+
+    split_ikey = nr->ikey0_[0];
+    return p >= mid ? 1 + (mid == width) : 0;
+}
+
+template <typename P>
+int internode<P>::split_into(internode<P> *nr, int p, ikey_type ka,
+                             node_base<P> *value, ikey_type& split_ikey,
+                             int split_type)
+{
+    // B+tree internal node insertion.
+    // Split *this, with items [0,T::width), into *this + nr, simultaneously
+    // inserting "ka:value" at position "p" (0 <= p <= T::width).
+    // The midpoint element of the result is stored in "split_ikey".
+
+    // Let mid = ceil(T::width / 2). After the split, the key at
+    // post-insertion position mid is stored in split_ikey. *this contains keys
+    // [0,mid) and nr contains keys [mid+1,T::width+1).
+    // If p < mid, then x goes into *this, pre-insertion item mid-1 goes into
+    //   split_ikey, and the first element of nr is pre-insertion item mid.
+    // If p == mid, then x goes into split_ikey and the first element of
+    //   nr is pre-insertion item mid.
+    // If p > mid, then x goes into nr, pre-insertion item mid goes into
+    //   split_ikey, and the first element of nr is post-insertion item mid+1.
+    masstree_precondition(!this->concurrent || (this->locked() && nr->locked()));
+
+    int mid = (split_type == 2 ? this->width : (this->width + 1) / 2);
+    nr->nkeys_ = this->width + 1 - (mid + 1);
+
+    if (p < mid) {
+        nr->child_[0] = this->child_[mid];
+        nr->shift_from(0, this, mid, this->width - mid);
+        split_ikey = this->ikey0_[mid - 1];
+    } else if (p == mid) {
+        nr->child_[0] = value;
+        nr->shift_from(0, this, mid, this->width - mid);
+        split_ikey = ka;
+    } else {
+        nr->child_[0] = this->child_[mid + 1];
+        nr->shift_from(0, this, mid + 1, p - (mid + 1));
+        nr->assign(p - (mid + 1), ka, value);
+        nr->shift_from(p + 1 - (mid + 1), this, p, this->width - p);
+        split_ikey = this->ikey0_[mid];
+    }
+
+    for (int i = 0; i <= nr->nkeys_; ++i)
+        nr->child_[i]->set_parent(nr);
+
+    this->mark_split();
+    if (p < mid) {
+        this->nkeys_ = mid - 1;
+        return p;
+    } else {
+        this->nkeys_ = mid;
+        return -1;
+    }
+}
+
+
+template <typename P>
+bool tcursor<P>::make_split(threadinfo& ti)
+{
+    // We reach here if we might need to split, either because the node is
+    // full, or because we're trying to insert into position 0 (which holds
+    // the ikey_bound). But in the latter case, perhaps we can rearrange the
+    // permutation to do an insert instead.
+    if (n_->size() < n_->width) {
+        permuter_type perm(n_->permutation_);
+        perm.exchange(perm.size(), n_->width - 1);
+        kx_.p = perm.back();
+        if (kx_.p != 0) {
+            n_->permutation_ = perm.value();
+            fence();
+            n_->assign(kx_.p, ka_, ti);
+            return false;
+        }
+    }
+
+    node_type* n = n_;
+    node_type* child = leaf_type::make(n_->ksuf_used_capacity(), n_->node_ts_, ti);
+    child->assign_version(*n_);
+    ikey_type xikey[2];
+    int split_type = n_->split_into(static_cast<leaf_type *>(child),
+                                    kx_.i, ka_, xikey[0], ti);
+    bool sense = false;
+
+    while (1) {
+        masstree_invariant(!n->concurrent || (n->locked() && child->locked() && (n->isleaf() || n->splitting())));
+        internode_type *next_child = 0;
+
+        internode_type *p = n->locked_parent(ti);
+
+        if (!node_type::parent_exists(p)) {
+            internode_type *nn = internode_type::make(ti);
+            nn->child_[0] = n;
+            nn->assign(0, xikey[sense], child);
+            nn->nkeys_ = 1;
+            nn->parent_ = p;
+            nn->mark_root();
+            fence();
+            n->set_parent(nn);
+        } else {
+            int kp = internode_type::bound_type::upper(xikey[sense], *p);
+
+            if (p->size() < p->width)
+                p->mark_insert();
+            else {
+                next_child = internode_type::make(ti);
+                next_child->assign_version(*p);
+                next_child->mark_nonroot();
+                kp = p->split_into(next_child, kp, xikey[sense],
+                                   child, xikey[!sense], split_type);
+            }
+
+            if (kp >= 0) {
+                p->shift_up(kp + 1, kp, p->size() - kp);
+                p->assign(kp, xikey[sense], child);
+                fence();
+                ++p->nkeys_;
+            }
+        }
+
+        if (n->isleaf()) {
+            leaf_type *nl = static_cast<leaf_type *>(n);
+            leaf_type *nr = static_cast<leaf_type *>(child);
+            permuter_type perml(nl->permutation_);
+            int width = perml.size();
+            perml.set_size(width - nr->size());
+            // removed item, if any, must be @ perml.size()
+            if (width != nl->width)
+                perml.exchange(perml.size(), nl->width - 1);
+            nl->mark_split();
+            nl->permutation_ = perml.value();
+            if (split_type == 0) {
+                kx_.p = perml.back();
+                nl->assign(kx_.p, ka_, ti);
+            } else {
+                kx_.i = kx_.p = kx_.i - perml.size();
+                n_ = nr;
+            }
+            // versions/sizes shouldn't change after this
+            if (nl != n_) {
+                assert(nr == n_);
+                // we don't add n_ until lp.finish() is called (this avoids next_version_value() annoyances)
+                updated_v_ = nl->full_unlocked_version_value();
+            } else
+                new_nodes_.emplace_back(nr, nr->full_unlocked_version_value());
+        }
+
+        if (n != n_)
+            n->unlock();
+        if (child != n_)
+            child->unlock();
+        if (next_child) {
+            n = p;
+            child = next_child;
+            sense = !sense;
+        } else if (p) {
+            p->unlock();
+            break;
+        } else
+            break;
+    }
+
+    return false;
+}
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/masstree_struct.hh b/silo/masstree/masstree_struct.hh
new file mode 100644 (file)
index 0000000..fd21d4f
--- /dev/null
@@ -0,0 +1,773 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_STRUCT_HH
+#define MASSTREE_STRUCT_HH
+#include "masstree.hh"
+#include "nodeversion.hh"
+#include "stringbag.hh"
+#include "mtcounters.hh"
+#include "timestamp.hh"
+namespace Masstree {
+
+template <typename P>
+struct make_nodeversion {
+    typedef typename mass::conditional<P::concurrent,
+                                       nodeversion,
+                                       singlethreaded_nodeversion>::type type;
+};
+
+template <typename P>
+struct make_prefetcher {
+    typedef typename mass::conditional<P::prefetch,
+                                       value_prefetcher<typename P::value_type>,
+                                       do_nothing>::type type;
+};
+
+template <typename P>
+class node_base : public make_nodeversion<P>::type {
+  public:
+    static constexpr bool concurrent = P::concurrent;
+    static constexpr int nikey = 1;
+    typedef leaf<P> leaf_type;
+    typedef internode<P> internode_type;
+    typedef node_base<P> base_type;
+    typedef typename P::value_type value_type;
+    typedef leafvalue<P> leafvalue_type;
+    typedef typename P::ikey_type ikey_type;
+    typedef key<ikey_type> key_type;
+    typedef typename make_nodeversion<P>::type nodeversion_type;
+    typedef typename P::threadinfo_type threadinfo;
+
+    node_base(bool isleaf)
+        : nodeversion_type(isleaf) {
+    }
+
+    int size() const {
+        if (this->isleaf())
+            return static_cast<const leaf_type*>(this)->size();
+        else
+            return static_cast<const internode_type*>(this)->size();
+    }
+
+    inline base_type* parent() const {
+        // almost always an internode
+        if (this->isleaf())
+            return static_cast<const leaf_type*>(this)->parent_;
+        else
+            return static_cast<const internode_type*>(this)->parent_;
+    }
+    static inline base_type* parent_for_layer_root(base_type* higher_layer) {
+        (void) higher_layer;
+        return 0;
+    }
+    static inline bool parent_exists(base_type* p) {
+        return p != 0;
+    }
+    inline bool has_parent() const {
+        return parent_exists(parent());
+    }
+    inline internode_type* locked_parent(threadinfo& ti) const;
+    inline void set_parent(base_type* p) {
+        if (this->isleaf())
+            static_cast<leaf_type*>(this)->parent_ = p;
+        else
+            static_cast<internode_type*>(this)->parent_ = p;
+    }
+    inline base_type* unsplit_ancestor() const {
+        base_type* x = const_cast<base_type*>(this), *p;
+        while (x->has_split() && (p = x->parent()))
+            x = p;
+        return x;
+    }
+    inline leaf_type* leftmost() const {
+        base_type* x = unsplit_ancestor();
+        while (!x->isleaf()) {
+            internode_type* in = static_cast<internode_type*>(x);
+            x = in->child_[0];
+        }
+        return x;
+    }
+
+    inline leaf_type* reach_leaf(const key_type& k, nodeversion_type& version,
+                                 threadinfo& ti) const;
+
+    void prefetch_full() const {
+        for (int i = 0; i < std::min(16 * std::min(P::leaf_width, P::internode_width) + 1, 4 * 64); i += 64)
+            ::prefetch((const char *) this + i);
+    }
+
+    void print(FILE* f, const char* prefix, int indent, int kdepth);
+};
+
+template <typename P>
+class internode : public node_base<P> {
+  public:
+    static constexpr int width = P::internode_width;
+    typedef typename node_base<P>::nodeversion_type nodeversion_type;
+    typedef key<typename P::ikey_type> key_type;
+    typedef typename P::ikey_type ikey_type;
+    typedef typename key_bound<width, P::bound_method>::type bound_type;
+    typedef typename P::threadinfo_type threadinfo;
+
+    uint8_t nkeys_;
+    ikey_type ikey0_[width];
+    node_base<P>* child_[width + 1];
+    node_base<P>* parent_;
+    kvtimestamp_t created_at_[P::debug_level > 0];
+
+    internode()
+        : node_base<P>(false), nkeys_(0), parent_() {
+    }
+
+    static internode<P>* make(threadinfo& ti) {
+        void* ptr = ti.pool_allocate(sizeof(internode<P>),
+                                     memtag_masstree_internode);
+        internode<P>* n = new(ptr) internode<P>;
+        assert(n);
+        if (P::debug_level > 0)
+            n->created_at_[0] = ti.operation_timestamp();
+        return n;
+    }
+
+    int size() const {
+        return nkeys_;
+    }
+
+    key_type get_key(int p) const {
+        return key_type(ikey0_[p]);
+    }
+    ikey_type ikey(int p) const {
+        return ikey0_[p];
+    }
+    int compare_key(ikey_type a, int bp) const {
+        return ::compare(a, ikey(bp));
+    }
+    int compare_key(const key_type& a, int bp) const {
+        return ::compare(a.ikey(), ikey(bp));
+    }
+    inline int stable_last_key_compare(const key_type& k, nodeversion_type v,
+                                       threadinfo& ti) const;
+
+    void prefetch() const {
+        for (int i = 64; i < std::min(16 * width + 1, 4 * 64); i += 64)
+            ::prefetch((const char *) this + i);
+    }
+
+    void print(FILE* f, const char* prefix, int indent, int kdepth);
+
+    void deallocate(threadinfo& ti) {
+        ti.pool_deallocate(this, sizeof(*this), memtag_masstree_internode);
+    }
+    void deallocate_rcu(threadinfo& ti) {
+        ti.pool_deallocate_rcu(this, sizeof(*this), memtag_masstree_internode);
+    }
+
+  private:
+    void assign(int p, ikey_type ikey, node_base<P>* child) {
+        child->set_parent(this);
+        child_[p + 1] = child;
+        ikey0_[p] = ikey;
+    }
+
+    void shift_from(int p, const internode<P>* x, int xp, int n) {
+        masstree_precondition(x != this);
+        if (n) {
+            memcpy(ikey0_ + p, x->ikey0_ + xp, sizeof(ikey0_[0]) * n);
+            memcpy(child_ + p + 1, x->child_ + xp + 1, sizeof(child_[0]) * n);
+        }
+    }
+    void shift_up(int p, int xp, int n) {
+        memmove(ikey0_ + p, ikey0_ + xp, sizeof(ikey0_[0]) * n);
+        for (node_base<P> **a = child_ + p + n, **b = child_ + xp + n; n; --a, --b, --n)
+            *a = *b;
+    }
+    void shift_down(int p, int xp, int n) {
+        memmove(ikey0_ + p, ikey0_ + xp, sizeof(ikey0_[0]) * n);
+        for (node_base<P> **a = child_ + p + 1, **b = child_ + xp + 1; n; ++a, ++b, --n)
+            *a = *b;
+    }
+
+    int split_into(internode<P>* nr, int p, ikey_type ka, node_base<P>* value,
+                   ikey_type& split_ikey, int split_type);
+
+    template <typename PP> friend class tcursor;
+};
+
+template <typename P>
+class leafvalue {
+  public:
+    typedef typename P::value_type value_type;
+    typedef typename make_prefetcher<P>::type prefetcher_type;
+
+    leafvalue() {
+    }
+    leafvalue(value_type v) {
+        u_.v = v;
+    }
+    leafvalue(node_base<P>* n) {
+        u_.x = reinterpret_cast<uintptr_t>(n);
+    }
+
+    static leafvalue<P> make_empty() {
+        return leafvalue<P>(value_type());
+    }
+
+    typedef bool (leafvalue<P>::*unspecified_bool_type)() const;
+    operator unspecified_bool_type() const {
+        return u_.x ? &leafvalue<P>::empty : 0;
+    }
+    bool empty() const {
+        return !u_.x;
+    }
+
+    value_type value() const {
+        return u_.v;
+    }
+    value_type& value() {
+        return u_.v;
+    }
+
+    node_base<P>* layer() const {
+        return reinterpret_cast<node_base<P>*>(u_.x);
+    }
+
+    void prefetch(int keylenx) const {
+        if (!leaf<P>::keylenx_is_layer(keylenx))
+            prefetcher_type()(u_.v);
+        else
+            u_.n->prefetch_full();
+    }
+
+  private:
+    union {
+        node_base<P>* n;
+        value_type v;
+        uintptr_t x;
+    } u_;
+};
+
+template <typename P>
+class leaf : public node_base<P> {
+  public:
+    static constexpr int width = P::leaf_width;
+    typedef typename node_base<P>::nodeversion_type nodeversion_type;
+    typedef key<typename P::ikey_type> key_type;
+    typedef typename node_base<P>::leafvalue_type leafvalue_type;
+    typedef kpermuter<P::leaf_width> permuter_type;
+    typedef typename P::ikey_type ikey_type;
+    typedef typename key_bound<width, P::bound_method>::type bound_type;
+    typedef typename P::threadinfo_type threadinfo;
+    typedef stringbag<uint8_t> internal_ksuf_type;
+    typedef stringbag<uint16_t> external_ksuf_type;
+    static constexpr int ksuf_keylenx = 64;
+    static constexpr int layer_keylenx = 128;
+
+    enum {
+        modstate_insert = 0, modstate_remove = 1, modstate_deleted_layer = 2
+    };
+
+    int8_t extrasize64_;
+    uint8_t modstate_;
+    uint8_t keylenx_[width];
+    typename permuter_type::storage_type permutation_;
+    ikey_type ikey0_[width];
+    leafvalue_type lv_[width];
+    external_ksuf_type* ksuf_;
+    union {
+        leaf<P>* ptr;
+        uintptr_t x;
+    } next_;
+    leaf<P>* prev_;
+    node_base<P>* parent_;
+    kvtimestamp_t node_ts_;
+    kvtimestamp_t created_at_[P::debug_level > 0];
+    internal_ksuf_type iksuf_[0];
+
+    leaf(size_t sz, kvtimestamp_t node_ts)
+        : node_base<P>(true), modstate_(modstate_insert),
+          permutation_(permuter_type::make_empty()),
+          ksuf_(), parent_(), node_ts_(node_ts), iksuf_{} {
+        masstree_precondition(sz % 64 == 0 && sz / 64 < 128);
+        extrasize64_ = (int(sz) >> 6) - ((int(sizeof(*this)) + 63) >> 6);
+        if (extrasize64_ > 0)
+            new((void *)&iksuf_[0]) internal_ksuf_type(width, sz - sizeof(*this));
+    }
+
+    static leaf<P>* make(int ksufsize, kvtimestamp_t node_ts, threadinfo& ti) {
+        size_t sz = iceil(sizeof(leaf<P>) + std::min(ksufsize, 128), 64);
+        void* ptr = ti.pool_allocate(sz, memtag_masstree_leaf);
+        leaf<P>* n = new(ptr) leaf<P>(sz, node_ts);
+        assert(n);
+        if (P::debug_level > 0)
+            n->created_at_[0] = ti.operation_timestamp();
+        return n;
+    }
+    static leaf<P>* make_root(int ksufsize, leaf<P>* parent, threadinfo& ti) {
+        leaf<P>* n = make(ksufsize, parent ? parent->node_ts_ : 0, ti);
+        n->next_.ptr = n->prev_ = 0;
+        n->parent_ = node_base<P>::parent_for_layer_root(parent);
+        n->mark_root();
+        return n;
+    }
+
+    static size_t min_allocated_size() {
+        return (sizeof(leaf<P>) + 63) & ~size_t(63);
+    }
+    size_t allocated_size() const {
+        int es = (extrasize64_ >= 0 ? extrasize64_ : -extrasize64_ - 1);
+        return (sizeof(*this) + es * 64 + 63) & ~size_t(63);
+    }
+    int size() const {
+        return permuter_type::size(permutation_);
+    }
+    permuter_type permutation() const {
+        return permuter_type(permutation_);
+    }
+    typename nodeversion_type::value_type full_version_value() const {
+        static_assert(int(nodeversion_type::traits_type::top_stable_bits) >= int(permuter_type::size_bits), "not enough bits to add size to version");
+        return (this->version_value() << permuter_type::size_bits) + size();
+    }
+    typename nodeversion_type::value_type full_unlocked_version_value() const {
+        static_assert(int(nodeversion_type::traits_type::top_stable_bits) >= int(permuter_type::size_bits), "not enough bits to add size to version");
+        typename node_base<P>::nodeversion_type v(*this);
+        if (v.locked())
+            // subtlely, unlocked_version_value() is different than v.unlock(); v.version_value() because the latter will add a
+            // split bit if we're doing a split. So we do the latter to get the fully correct version.
+            v.unlock();
+        return (v.version_value() << permuter_type::size_bits) + size();
+    }
+
+    using node_base<P>::has_changed;
+    bool has_changed(nodeversion_type oldv,
+                     typename permuter_type::storage_type oldperm) const {
+        return this->has_changed(oldv) || oldperm != permutation_;
+    }
+
+    key_type get_key(int p) const {
+        int keylenx = keylenx_[p];
+        if (!keylenx_has_ksuf(keylenx))
+            return key_type(ikey0_[p], keylenx);
+        else
+            return key_type(ikey0_[p], ksuf(p));
+    }
+    ikey_type ikey(int p) const {
+        return ikey0_[p];
+    }
+    ikey_type ikey_bound() const {
+        return ikey0_[0];
+    }
+    int compare_key(const key_type& a, int bp) const {
+        return a.compare(ikey(bp), keylenx_[bp]);
+    }
+    inline int stable_last_key_compare(const key_type& k, nodeversion_type v,
+                                       threadinfo& ti) const;
+
+    inline leaf<P>* advance_to_key(const key_type& k, nodeversion_type& version,
+                                   threadinfo& ti) const;
+
+    static bool keylenx_is_layer(int keylenx) {
+        return keylenx > 127;
+    }
+    static bool keylenx_has_ksuf(int keylenx) {
+        return keylenx == ksuf_keylenx;
+    }
+
+    bool is_layer(int p) const {
+        return keylenx_is_layer(keylenx_[p]);
+    }
+    bool has_ksuf(int p) const {
+        return keylenx_has_ksuf(keylenx_[p]);
+    }
+    Str ksuf(int p, int keylenx) const {
+        masstree_precondition(keylenx_has_ksuf(keylenx));
+        return ksuf_ ? ksuf_->get(p) : iksuf_[0].get(p);
+    }
+    Str ksuf(int p) const {
+        return ksuf(p, keylenx_[p]);
+    }
+    bool ksuf_equals(int p, const key_type& ka) const {
+        return ksuf_equals(p, ka, keylenx_[p]);
+    }
+    bool ksuf_equals(int p, const key_type& ka, int keylenx) const {
+        if (!keylenx_has_ksuf(keylenx))
+            return true;
+        Str s = ksuf(p, keylenx);
+        return s.len == ka.suffix().len
+            && string_slice<uintptr_t>::equals_sloppy(s.s, ka.suffix().s, s.len);
+    }
+    // Returns 1 if match & not layer, 0 if no match, <0 if match and layer
+    int ksuf_matches(int p, const key_type& ka) const {
+        int keylenx = keylenx_[p];
+        if (keylenx < ksuf_keylenx)
+            return 1;
+        if (keylenx == layer_keylenx)
+            return -(int) sizeof(ikey_type);
+        Str s = ksuf(p, keylenx);
+        return s.len == ka.suffix().len
+            && string_slice<uintptr_t>::equals_sloppy(s.s, ka.suffix().s, s.len);
+    }
+    int ksuf_compare(int p, const key_type& ka) const {
+        int keylenx = keylenx_[p];
+        if (!keylenx_has_ksuf(keylenx))
+            return 0;
+        return ksuf(p, keylenx).compare(ka.suffix());
+    }
+
+    size_t ksuf_used_capacity() const {
+        if (ksuf_)
+            return ksuf_->used_capacity();
+        else if (extrasize64_ > 0)
+            return iksuf_[0].used_capacity();
+        else
+            return 0;
+    }
+    size_t ksuf_capacity() const {
+        if (ksuf_)
+            return ksuf_->capacity();
+        else if (extrasize64_ > 0)
+            return iksuf_[0].capacity();
+        else
+            return 0;
+    }
+    bool ksuf_external() const {
+        return ksuf_;
+    }
+    Str ksuf_storage(int p) const {
+        if (ksuf_)
+            return ksuf_->get(p);
+        else if (extrasize64_ > 0)
+            return iksuf_[0].get(p);
+        else
+            return Str();
+    }
+
+    bool deleted_layer() const {
+        return modstate_ == modstate_deleted_layer;
+    }
+
+    void prefetch() const {
+        for (int i = 64; i < std::min(16 * width + 1, 4 * 64); i += 64)
+            ::prefetch((const char *) this + i);
+        if (extrasize64_ > 0)
+            ::prefetch((const char *) &iksuf_[0]);
+        else if (extrasize64_ < 0) {
+            ::prefetch((const char *) ksuf_);
+            ::prefetch((const char *) ksuf_ + CACHE_LINE_SIZE);
+        }
+    }
+
+    void print(FILE* f, const char* prefix, int indent, int kdepth);
+
+    leaf<P>* safe_next() const {
+        return reinterpret_cast<leaf<P>*>(next_.x & ~(uintptr_t) 1);
+    }
+
+    void deallocate(threadinfo& ti) {
+        if (ksuf_)
+            ti.deallocate(ksuf_, ksuf_->capacity(),
+                          memtag_masstree_ksuffixes);
+        if (extrasize64_ != 0)
+            iksuf_[0].~stringbag();
+        ti.pool_deallocate(this, allocated_size(), memtag_masstree_leaf);
+    }
+    void deallocate_rcu(threadinfo& ti) {
+        if (ksuf_)
+            ti.deallocate_rcu(ksuf_, ksuf_->capacity(),
+                              memtag_masstree_ksuffixes);
+        ti.pool_deallocate_rcu(this, allocated_size(), memtag_masstree_leaf);
+    }
+
+  private:
+    inline void mark_deleted_layer() {
+        modstate_ = modstate_deleted_layer;
+    }
+
+    inline void assign(int p, const key_type& ka, threadinfo& ti) {
+        lv_[p] = leafvalue_type::make_empty();
+        ikey0_[p] = ka.ikey();
+        if (!ka.has_suffix())
+            keylenx_[p] = ka.length();
+        else {
+            keylenx_[p] = ksuf_keylenx;
+            assign_ksuf(p, ka.suffix(), false, ti);
+        }
+    }
+    inline void assign_initialize(int p, const key_type& ka, threadinfo& ti) {
+        lv_[p] = leafvalue_type::make_empty();
+        ikey0_[p] = ka.ikey();
+        if (!ka.has_suffix())
+            keylenx_[p] = ka.length();
+        else {
+            keylenx_[p] = ksuf_keylenx;
+            assign_ksuf(p, ka.suffix(), true, ti);
+        }
+    }
+    inline void assign_initialize(int p, leaf<P>* x, int xp, threadinfo& ti) {
+        lv_[p] = x->lv_[xp];
+        ikey0_[p] = x->ikey0_[xp];
+        keylenx_[p] = x->keylenx_[xp];
+        if (x->has_ksuf(xp))
+            assign_ksuf(p, x->ksuf(xp), true, ti);
+    }
+    inline void assign_initialize_for_layer(int p, const key_type& ka) {
+        assert(ka.has_suffix());
+        ikey0_[p] = ka.ikey();
+        keylenx_[p] = layer_keylenx;
+    }
+    void assign_ksuf(int p, Str s, bool initializing, threadinfo& ti);
+
+    inline ikey_type ikey_after_insert(const permuter_type& perm, int i,
+                                       const key_type& ka, int ka_i) const;
+    int split_into(leaf<P>* nr, int p, const key_type& ka, ikey_type& split_ikey,
+                   threadinfo& ti);
+
+    template <typename PP> friend class tcursor;
+};
+
+
+template <typename P>
+void basic_table<P>::initialize(threadinfo& ti) {
+    masstree_precondition(!root_);
+    root_ = node_type::leaf_type::make_root(0, 0, ti);
+}
+
+
+/** @brief Return this node's parent in locked state.
+    @pre this->locked()
+    @post this->parent() == result && (!result || result->locked()) */
+template <typename P>
+internode<P>* node_base<P>::locked_parent(threadinfo& ti) const
+{
+    node_base<P>* p;
+    masstree_precondition(!this->concurrent || this->locked());
+    while (1) {
+        p = this->parent();
+        if (!node_base<P>::parent_exists(p))
+            break;
+        nodeversion_type pv = p->lock(*p, ti.lock_fence(tc_internode_lock));
+        if (p == this->parent()) {
+            masstree_invariant(!p->isleaf());
+            break;
+        }
+        p->unlock(pv);
+        relax_fence();
+    }
+    return static_cast<internode<P>*>(p);
+}
+
+
+/** @brief Return the result of compare_key(k, LAST KEY IN NODE).
+
+    Reruns the comparison until a stable comparison is obtained. */
+template <typename P>
+inline int
+internode<P>::stable_last_key_compare(const key_type& k, nodeversion_type v,
+                                      threadinfo& ti) const
+{
+    while (1) {
+        int cmp = compare_key(k, size() - 1);
+        if (likely(!this->has_changed(v)))
+            return cmp;
+        v = this->stable_annotated(ti.stable_fence());
+    }
+}
+
+template <typename P>
+inline int
+leaf<P>::stable_last_key_compare(const key_type& k, nodeversion_type v,
+                                 threadinfo& ti) const
+{
+    while (1) {
+        typename leaf<P>::permuter_type perm(permutation_);
+        int p = perm[perm.size() - 1];
+        int cmp = compare_key(k, p);
+        if (likely(!this->has_changed(v)))
+            return cmp;
+        v = this->stable_annotated(ti.stable_fence());
+    }
+}
+
+
+/** @brief Return the leaf in this tree layer responsible for @a ka.
+
+    Returns a stable leaf. Sets @a version to the stable version. */
+template <typename P>
+inline leaf<P>* node_base<P>::reach_leaf(const key_type& ka,
+                                         nodeversion_type& version,
+                                         threadinfo& ti) const
+{
+    const node_base<P> *n[2];
+    typename node_base<P>::nodeversion_type v[2];
+    bool sense;
+
+    // Get a non-stale root.
+    // Detect staleness by checking whether n has ever split.
+    // The true root has never split.
+ retry:
+    sense = false;
+    n[sense] = this;
+    while (1) {
+        v[sense] = n[sense]->stable_annotated(ti.stable_fence());
+        if (!v[sense].has_split())
+            break;
+        ti.mark(tc_root_retry);
+        n[sense] = n[sense]->unsplit_ancestor();
+    }
+
+    // Loop over internal nodes.
+    while (!v[sense].isleaf()) {
+        const internode<P> *in = static_cast<const internode<P> *>(n[sense]);
+        in->prefetch();
+        int kp = internode<P>::bound_type::upper(ka, *in);
+        n[!sense] = in->child_[kp];
+        if (!n[!sense])
+            goto retry;
+        v[!sense] = n[!sense]->stable_annotated(ti.stable_fence());
+
+        if (likely(!in->has_changed(v[sense]))) {
+            sense = !sense;
+            continue;
+        }
+
+        typename node_base<P>::nodeversion_type oldv = v[sense];
+        v[sense] = in->stable_annotated(ti.stable_fence());
+        if (oldv.has_split(v[sense])
+            && in->stable_last_key_compare(ka, v[sense], ti) > 0) {
+            ti.mark(tc_root_retry);
+            goto retry;
+        } else
+            ti.mark(tc_internode_retry);
+    }
+
+    version = v[sense];
+    return const_cast<leaf<P> *>(static_cast<const leaf<P> *>(n[sense]));
+}
+
+/** @brief Return the leaf at or after *this responsible for @a ka.
+    @pre *this was responsible for @a ka at version @a v
+
+    Checks whether *this has split since version @a v. If it has split, then
+    advances through the leaves using the B^link-tree pointers and returns
+    the relevant leaf, setting @a v to the stable version for that leaf. */
+template <typename P>
+leaf<P>* leaf<P>::advance_to_key(const key_type& ka, nodeversion_type& v,
+                                 threadinfo& ti) const
+{
+    const leaf<P>* n = this;
+    nodeversion_type oldv = v;
+    v = n->stable_annotated(ti.stable_fence());
+    if (v.has_split(oldv)
+        && n->stable_last_key_compare(ka, v, ti) > 0) {
+        leaf<P> *next;
+        ti.mark(tc_leaf_walk);
+        while (likely(!v.deleted()) && (next = n->safe_next())
+               && compare(ka.ikey(), next->ikey_bound()) >= 0) {
+            n = next;
+            v = n->stable_annotated(ti.stable_fence());
+        }
+    }
+    return const_cast<leaf<P>*>(n);
+}
+
+
+/** @brief Assign position @a p's keysuffix to @a s.
+
+    This may allocate a new suffix container, copying suffixes over.
+
+    The @a initializing parameter determines which suffixes are copied. If @a
+    initializing is false, then this is an insertion into a live node. The
+    live node's permutation indicates which keysuffixes are active, and only
+    active suffixes are copied. If @a initializing is true, then this
+    assignment is part of the initialization process for a new node. The
+    permutation might not be set up yet. In this case, it is assumed that key
+    positions [0,p) are ready: keysuffixes in that range are copied. In either
+    case, the key at position p is NOT copied; it is assigned to @a s. */
+template <typename P>
+void leaf<P>::assign_ksuf(int p, Str s, bool initializing, threadinfo& ti) {
+    if ((ksuf_ && ksuf_->assign(p, s))
+        || (extrasize64_ > 0 && iksuf_[0].assign(p, s)))
+        return;
+
+    external_ksuf_type* oksuf = ksuf_;
+
+    permuter_type perm(permutation_);
+    int n = initializing ? p : perm.size();
+
+    size_t csz = 0;
+    for (int i = 0; i < n; ++i) {
+        int mp = initializing ? i : perm[i];
+        if (mp != p && has_ksuf(mp))
+            csz += ksuf(mp).len;
+    }
+
+    size_t sz = iceil_log2(external_ksuf_type::safe_size(width, csz + s.len));
+    if (oksuf)
+        sz = std::max(sz, oksuf->capacity());
+
+    void* ptr = ti.allocate(sz, memtag_masstree_ksuffixes);
+    external_ksuf_type* nksuf = new(ptr) external_ksuf_type(width, sz);
+    for (int i = 0; i < n; ++i) {
+        int mp = initializing ? i : perm[i];
+        if (mp != p && has_ksuf(mp)) {
+            bool ok = nksuf->assign(mp, ksuf(mp));
+            assert(ok); (void) ok;
+        }
+    }
+    bool ok = nksuf->assign(p, s);
+    assert(ok); (void) ok;
+    fence();
+
+    // removed ksufs aren't copied to the new ksuf, but observers
+    // might need them. We ensure that observers must retry by
+    // ensuring that we are not currently in the remove state.
+    // State transitions are accompanied by mark_insert() so observers
+    // will retry.
+    masstree_invariant(modstate_ != modstate_remove);
+
+    ksuf_ = nksuf;
+    fence();
+
+    if (extrasize64_ >= 0)      // now the new ksuf_ installed, mark old dead
+        extrasize64_ = -extrasize64_ - 1;
+
+    if (oksuf)
+        ti.deallocate_rcu(oksuf, oksuf->capacity(),
+                          memtag_masstree_ksuffixes);
+}
+
+template <typename P>
+inline basic_table<P>::basic_table()
+    : root_(0) {
+}
+
+template <typename P>
+inline node_base<P>* basic_table<P>::root() const {
+    return root_;
+}
+
+template <typename P>
+inline node_base<P>* basic_table<P>::fix_root() {
+    node_base<P>* root = root_;
+    if (unlikely(root->has_split())) {
+        node_base<P>* old_root = root;
+        root = root->unsplit_ancestor();
+        (void) cmpxchg(&root_, old_root, root);
+    }
+    return root;
+}
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/masstree_tcursor.hh b/silo/masstree/masstree_tcursor.hh
new file mode 100644 (file)
index 0000000..f5cc99e
--- /dev/null
@@ -0,0 +1,228 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_TCURSOR_HH
+#define MASSTREE_TCURSOR_HH 1
+#include "local_vector.hh"
+#include "masstree_key.hh"
+#include "masstree_struct.hh"
+namespace Masstree {
+template <typename P> struct gc_layer_rcu_callback;
+
+template <typename P>
+class unlocked_tcursor {
+  public:
+    typedef typename P::value_type value_type;
+    typedef key<typename P::ikey_type> key_type;
+    typedef typename P::threadinfo_type threadinfo;
+    typedef typename leaf<P>::nodeversion_type nodeversion_type;
+    typedef typename nodeversion_type::value_type nodeversion_value_type;
+    typedef typename leaf<P>::permuter_type permuter_type;
+
+    inline unlocked_tcursor(const basic_table<P>& table, Str str)
+        : ka_(str), lv_(leafvalue<P>::make_empty()),
+          root_(table.root()) {
+    }
+    inline unlocked_tcursor(basic_table<P>& table, Str str)
+        : ka_(str), lv_(leafvalue<P>::make_empty()),
+          root_(table.fix_root()) {
+    }
+    inline unlocked_tcursor(const basic_table<P>& table,
+                            const char* s, int len)
+        : ka_(s, len), lv_(leafvalue<P>::make_empty()),
+          root_(table.root()) {
+    }
+    inline unlocked_tcursor(basic_table<P>& table,
+                            const char* s, int len)
+        : ka_(s, len), lv_(leafvalue<P>::make_empty()),
+          root_(table.fix_root()) {
+    }
+    inline unlocked_tcursor(const basic_table<P>& table,
+                            const unsigned char* s, int len)
+        : ka_(reinterpret_cast<const char*>(s), len),
+          lv_(leafvalue<P>::make_empty()), root_(table.root()) {
+    }
+    inline unlocked_tcursor(basic_table<P>& table,
+                            const unsigned char* s, int len)
+        : ka_(reinterpret_cast<const char*>(s), len),
+          lv_(leafvalue<P>::make_empty()), root_(table.fix_root()) {
+    }
+
+    bool find_unlocked(threadinfo& ti);
+
+    inline value_type value() const {
+        return lv_.value();
+    }
+    inline leaf<P>* node() const {
+        return n_;
+    }
+    inline permuter_type permutation() const {
+        return perm_;
+    }
+    inline int compare_key(const key_type& a, int bp) const {
+        return n_->compare_key(a, bp);
+    }
+    inline nodeversion_value_type full_version_value() const {
+        static_assert(int(nodeversion_type::traits_type::top_stable_bits) >= int(leaf<P>::permuter_type::size_bits), "not enough bits to add size to version");
+        return (v_.version_value() << leaf<P>::permuter_type::size_bits) + perm_.size();
+    }
+
+  private:
+    leaf<P>* n_;
+    key_type ka_;
+    typename leaf<P>::nodeversion_type v_;
+    permuter_type perm_;
+    leafvalue<P> lv_;
+    const node_base<P>* root_;
+};
+
+template <typename P>
+class tcursor {
+  public:
+    typedef node_base<P> node_type;
+    typedef leaf<P> leaf_type;
+    typedef internode<P> internode_type;
+    typedef typename P::value_type value_type;
+    typedef leafvalue<P> leafvalue_type;
+    typedef typename leaf_type::permuter_type permuter_type;
+    typedef typename P::ikey_type ikey_type;
+    typedef key<ikey_type> key_type;
+    typedef typename leaf<P>::nodeversion_type nodeversion_type;
+    typedef typename nodeversion_type::value_type nodeversion_value_type;
+    typedef typename P::threadinfo_type threadinfo;
+    static constexpr int new_nodes_size = 1; // unless we make a new trie newnodes will have at most 1 item
+    typedef local_vector<std::pair<leaf_type*, nodeversion_value_type>, new_nodes_size> new_nodes_type;
+
+    tcursor(basic_table<P>& table, Str str)
+        : ka_(str), root_(table.fix_root()) {
+    }
+    tcursor(basic_table<P>& table, const char* s, int len)
+        : ka_(s, len), root_(table.fix_root()) {
+    }
+    tcursor(basic_table<P>& table, const unsigned char* s, int len)
+        : ka_(reinterpret_cast<const char*>(s), len), root_(table.fix_root()) {
+    }
+    tcursor(node_base<P>* root, const char* s, int len)
+        : ka_(s, len), root_(root) {
+    }
+    tcursor(node_base<P>* root, const unsigned char* s, int len)
+        : ka_(reinterpret_cast<const char*>(s), len), root_(root) {
+    }
+
+    inline bool has_value() const {
+        return kx_.p >= 0;
+    }
+    inline value_type &value() const {
+        return n_->lv_[kx_.p].value();
+    }
+
+    inline bool is_first_layer() const {
+        return !ka_.is_shifted();
+    }
+
+    inline leaf<P>* node() const {
+        return n_;
+    }
+    inline kvtimestamp_t node_timestamp() const {
+        return n_->node_ts_;
+    }
+    inline kvtimestamp_t &node_timestamp() {
+        return n_->node_ts_;
+    }
+
+    inline leaf_type *original_node() const {
+        return original_n_;
+    }
+
+    inline nodeversion_value_type original_version_value() const {
+        return original_v_;
+    }
+
+    inline nodeversion_value_type updated_version_value() const {
+        return updated_v_;
+    }
+
+    inline const new_nodes_type &new_nodes() const {
+        return new_nodes_;
+    }
+
+    inline bool find_locked(threadinfo& ti);
+    inline bool find_insert(threadinfo& ti);
+
+    inline void finish(int answer, threadinfo& ti);
+
+    inline nodeversion_value_type previous_full_version_value() const;
+    inline nodeversion_value_type next_full_version_value(int state) const;
+
+  private:
+    leaf_type *n_;
+    key_type ka_;
+    key_indexed_position kx_;
+    node_base<P>* root_;
+    int state_;
+
+    leaf_type *original_n_;
+    nodeversion_value_type original_v_;
+    nodeversion_value_type updated_v_;
+    new_nodes_type new_nodes_;
+
+    inline node_type* reset_retry() {
+        ka_.unshift_all();
+        return root_;
+    }
+
+    bool make_new_layer(threadinfo& ti);
+    bool make_split(threadinfo& ti);
+    inline void finish_insert();
+    inline bool finish_remove(threadinfo& ti);
+
+    static void collapse(internode_type* p, ikey_type ikey,
+                         node_type* root, Str prefix, threadinfo& ti);
+    /** Remove @a leaf from the Masstree rooted at @a rootp.
+     * @param prefix String defining the path to the tree containing this leaf.
+     *   If removing a leaf in layer 0, @a prefix is empty.
+     *   If removing, for example, the node containing key "01234567ABCDEF" in the layer-1 tree
+     *   rooted at "01234567", then @a prefix should equal "01234567". */
+    static bool remove_leaf(leaf_type* leaf, node_type* root,
+                            Str prefix, threadinfo& ti);
+
+    bool gc_layer(threadinfo& ti);
+    friend struct gc_layer_rcu_callback<P>;
+};
+
+template <typename P>
+inline typename tcursor<P>::nodeversion_value_type
+tcursor<P>::previous_full_version_value() const {
+    static_assert(int(nodeversion_type::traits_type::top_stable_bits) >= int(leaf<P>::permuter_type::size_bits), "not enough bits to add size to version");
+    return (n_->unlocked_version_value() << leaf<P>::permuter_type::size_bits) + n_->size();
+}
+
+template <typename P>
+inline typename tcursor<P>::nodeversion_value_type
+tcursor<P>::next_full_version_value(int state) const {
+    static_assert(int(nodeversion_type::traits_type::top_stable_bits) >= int(leaf<P>::permuter_type::size_bits), "not enough bits to add size to version");
+    typename node_base<P>::nodeversion_type v(*n_);
+    v.unlock();
+    nodeversion_value_type result = (v.version_value() << leaf<P>::permuter_type::size_bits) + n_->size();
+    if (state < 0 && (state_ & 1))
+        return result - 1;
+    else if (state > 0 && state_ == 2)
+        return result + 1;
+    else
+        return result;
+}
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/misc.cc b/silo/masstree/misc.cc
new file mode 100644 (file)
index 0000000..b8a4722
--- /dev/null
@@ -0,0 +1,40 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "misc.hh"
+#include <unistd.h>
+#include "kvthread.hh"
+
+int clp_parse_suffixdouble(Clp_Parser *clp, const char *vstr,
+                          int complain, void *)
+{
+    const char *post;
+    if (*vstr == 0 || isspace((unsigned char) *vstr))
+       post = vstr;
+    else
+       clp->val.d = strtod(vstr, (char **) &post);
+    if (vstr != post && (*post == 'K' || *post == 'k'))
+       clp->val.d *= 1000, ++post;
+    else if (vstr != post && (*post == 'M' || *post == 'm'))
+       clp->val.d *= 1000000, ++post;
+    else if (vstr != post && (*post == 'B' || *post == 'b' || *post == 'G' || *post == 'g'))
+       clp->val.d *= 1000000000, ++post;
+    if (*vstr != 0 && *post == 0)
+       return 1;
+    else if (complain)
+       return Clp_OptionError(clp, "%<%O%> expects a real number, not %<%s%>", vstr);
+    else
+       return 0;
+}
diff --git a/silo/masstree/misc.hh b/silo/masstree/misc.hh
new file mode 100644 (file)
index 0000000..ccbdcef
--- /dev/null
@@ -0,0 +1,86 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MISC_HH
+#define MISC_HH
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
+#include <math.h>
+#include "str.hh"
+#include "timestamp.hh"
+#include "clp.h"
+
+inline void xalarm(double d) {
+    double ip, fp = modf(d, &ip);
+    struct itimerval x;
+    timerclear(&x.it_interval);
+    x.it_value.tv_sec = (long) ip;
+    x.it_value.tv_usec = (long) (fp * 1000000);
+    setitimer(ITIMER_REAL, &x, 0);
+}
+
+inline void napms(int n) /* nap n milliseconds */
+{
+  int ret;
+  struct timespec req, rem;
+
+  req.tv_sec = n / 1000;
+  req.tv_nsec = (n % 1000) * 1000000;
+  ret = nanosleep(&req, &rem);
+  if(ret == -1 && errno != EINTR){
+    perror("nanosleep");
+    exit(EXIT_FAILURE);
+  }
+}
+
+struct quick_istr {
+    char buf_[32];
+    char *bbuf_;
+    quick_istr() {
+      set(0);
+    }
+    quick_istr(unsigned long x, int minlen = 0) {
+      set(x, minlen);
+    }
+    void set(unsigned long x, int minlen = 0){
+       bbuf_ = buf_ + sizeof(buf_) - 1;
+       do {
+           *--bbuf_ = (x % 10) + '0';
+           x /= 10;
+       } while (--minlen > 0 || x != 0);
+    }
+    lcdf::Str string() const {
+       return lcdf::Str(bbuf_, buf_ + sizeof(buf_) - 1);
+    }
+    const char *c_str() {
+       buf_[sizeof(buf_) - 1] = 0;
+       return bbuf_;
+    }
+    bool operator==(lcdf::Str s) const {
+       return s.len == (buf_ + sizeof(buf_) - 1) - bbuf_
+           && memcmp(s.s, bbuf_, s.len) == 0;
+    }
+    bool operator!=(lcdf::Str s) const {
+       return !(*this == s);
+    }
+};
+
+struct Clp_Parser;
+int clp_parse_suffixdouble(struct Clp_Parser *clp, const char *vstr,
+                          int complain, void *user_data);
+
+#endif
diff --git a/silo/masstree/msgpack.cc b/silo/masstree/msgpack.cc
new file mode 100644 (file)
index 0000000..4b8859b
--- /dev/null
@@ -0,0 +1,240 @@
+#include "msgpack.hh"
+namespace msgpack {
+
+namespace {
+const uint8_t nbytes[] = {
+    /* 0xC0-0xC3 */ 0, 0, 0, 0,
+    /* 0xC4-0xC6 fbin8-fbin32 */ 2, 3, 5,
+    /* 0xC7-0xC9 fext */ 0, 0, 0,
+    /* 0xCA ffloat32 */ 5,
+    /* 0xCB ffloat64 */ 9,
+    /* 0xCC-0xD3 ints */ 2, 3, 5, 9, 2, 3, 5, 9,
+    /* 0xD4-0xD8 ffixext */ 0, 0, 0, 0, 0,
+    /* 0xD9-0xDB fstr8-fstr32 */ 2, 3, 5,
+    /* 0xDC-0xDD farray16-farray32 */ 3, 5,
+    /* 0xDE-0xDF fmap16-fmap32 */ 3, 5
+};
+}
+
+const uint8_t* streaming_parser::consume(const uint8_t* first,
+                                         const uint8_t* last,
+                                         const String& str) {
+    using std::swap;
+    Json* jx;
+    int n = 0;
+
+    if (state_ < 0)
+        return first;
+    if (state_ == st_partial || state_ == st_string) {
+        int nneed;
+        if (state_ == st_partial)
+            nneed = nbytes[str_.udata()[0] - 0xC0];
+        else
+            nneed = stack_.back().size;
+        const uint8_t* next;
+        if (last - first < nneed - str_.length())
+            next = last;
+        else
+            next = first + (nneed - str_.length());
+        str_.append(first, next);
+        if (str_.length() != nneed)
+            return next;
+        first = next;
+        if (state_ == st_string) {
+            stack_.pop_back();
+            jx = stack_.empty() ? &json_ : stack_.back().jp;
+            *jx = str_;
+            goto next;
+        } else {
+            state_ = st_normal;
+            consume(str_.ubegin(), str_.uend(), str_);
+            if (state_ != st_normal)
+                return next;
+        }
+    }
+
+    while (first != last) {
+        jx = stack_.empty() ? &json_ : stack_.back().jp;
+
+        if (format::is_fixint(*first)) {
+            *jx = int(int8_t(*first));
+            ++first;
+        } else if (*first == format::fnull) {
+            *jx = Json();
+            ++first;
+        } else if (format::is_bool(*first)) {
+            *jx = bool(*first - format::ffalse);
+            ++first;
+        } else if (format::is_fixmap(*first)) {
+            n = *first - format::ffixmap;
+            ++first;
+        map:
+            if (jx->is_o())
+                jx->clear();
+            else
+                *jx = Json::make_object();
+        } else if (format::is_fixarray(*first)) {
+            n = *first - format::ffixarray;
+            ++first;
+        array:
+            if (!jx->is_a())
+                *jx = Json::make_array_reserve(n);
+            jx->resize(n);
+        } else if (format::is_fixstr(*first)) {
+            n = *first - format::ffixstr;
+            ++first;
+        raw:
+            if (last - first < n) {
+                str_ = String(first, last);
+                stack_.push_back(selem{0, n});
+                state_ = st_string;
+                return last;
+            }
+            if (first < str.ubegin() || first + n >= str.uend())
+                *jx = String(first, n);
+            else {
+                const char* s = reinterpret_cast<const char*>(first);
+                *jx = str.fast_substring(s, s + n);
+            }
+            first += n;
+        } else {
+            uint8_t type = *first - format::fnull;
+            if (!nbytes[type])
+                goto error;
+            if (last - first < nbytes[type]) {
+                str_ = String(first, last);
+                state_ = st_partial;
+                return last;
+            }
+            first += nbytes[type];
+            switch (type) {
+            case format::ffloat32 - format::fnull:
+                *jx = read_in_net_order<float>(first - 4);
+                break;
+            case format::ffloat64 - format::fnull:
+                *jx = read_in_net_order<double>(first - 8);
+                break;
+            case format::fuint8 - format::fnull:
+                *jx = int(first[-1]);
+                break;
+            case format::fuint16 - format::fnull:
+                *jx = read_in_net_order<uint16_t>(first - 2);
+                break;
+            case format::fuint32 - format::fnull:
+                *jx = read_in_net_order<uint32_t>(first - 4);
+                break;
+            case format::fuint64 - format::fnull:
+                *jx = read_in_net_order<uint64_t>(first - 8);
+                break;
+            case format::fint8 - format::fnull:
+                *jx = int8_t(first[-1]);
+                break;
+            case format::fint16 - format::fnull:
+                *jx = read_in_net_order<int16_t>(first - 2);
+                break;
+            case format::fint32 - format::fnull:
+                *jx = read_in_net_order<int32_t>(first - 4);
+                break;
+            case format::fint64 - format::fnull:
+                *jx = read_in_net_order<int64_t>(first - 8);
+                break;
+            case format::fbin8 - format::fnull:
+            case format::fstr8 - format::fnull:
+                n = first[-1];
+                goto raw;
+            case format::fbin16 - format::fnull:
+            case format::fstr16 - format::fnull:
+                n = read_in_net_order<uint16_t>(first - 2);
+                goto raw;
+            case format::fbin32 - format::fnull:
+            case format::fstr32 - format::fnull:
+                n = read_in_net_order<uint32_t>(first - 4);
+                goto raw;
+            case format::farray16 - format::fnull:
+                n = read_in_net_order<uint16_t>(first - 2);
+                goto array;
+            case format::farray32 - format::fnull:
+                n = read_in_net_order<uint32_t>(first - 4);
+                goto array;
+            case format::fmap16 - format::fnull:
+                n = read_in_net_order<uint16_t>(first - 2);
+                goto map;
+            case format::fmap32 - format::fnull:
+                n = read_in_net_order<uint32_t>(first - 4);
+                goto map;
+            }
+        }
+
+        // Add it
+    next:
+        if (jx == &jokey_) {
+            // Reading a key for some object Json
+            if (!jx->is_s() && !jx->is_i())
+                goto error;
+            selem* top = &stack_.back();
+            Json* jo = (top == stack_.begin() ? &json_ : top[-1].jp);
+            top->jp = &jo->get_insert(jx->to_s());
+            continue;
+        }
+
+        if (jx->is_a() && n != 0)
+            stack_.push_back(selem{&jx->at_insert(0), n - 1});
+        else if (jx->is_o() && n != 0)
+            stack_.push_back(selem{&jokey_, n - 1});
+        else {
+            while (!stack_.empty() && stack_.back().size == 0)
+                stack_.pop_back();
+            if (stack_.empty()) {
+                state_ = st_final;
+                return first;
+            }
+            selem* top = &stack_.back();
+            --top->size;
+            Json* jo = (top == stack_.begin() ? &json_ : top[-1].jp);
+            if (jo->is_a())
+                ++top->jp;
+            else
+                top->jp = &jokey_;
+        }
+    }
+
+    state_ = st_normal;
+    return first;
+
+ error:
+    state_ = st_error;
+    return first;
+}
+
+parser& parser::operator>>(Str& x) {
+    uint32_t len;
+    if ((uint32_t) *s_ - format::ffixstr < format::nfixstr) {
+        len = *s_ - format::ffixstr;
+        ++s_;
+    } else if (*s_ == format::fbin8 || *s_ == format::fstr8) {
+        len = s_[1];
+        s_ += 2;
+    } else if (*s_ == format::fbin16 || *s_ == format::fstr16) {
+        len = read_in_net_order<uint16_t>(s_ + 1);
+        s_ += 3;
+    } else {
+        assert(*s_ == format::fbin32 || *s_ == format::fstr32);
+        len = read_in_net_order<uint32_t>(s_ + 1);
+        s_ += 5;
+    }
+    x.assign(reinterpret_cast<const char*>(s_), len);
+    s_ += len;
+    return *this;
+}
+
+parser& parser::operator>>(String& x) {
+    Str s;
+    *this >> s;
+    if (str_)
+        x = str_.substring(s.begin(), s.end());
+    else
+        x.assign(s.begin(), s.end());
+    return *this;
+}
+
+} // namespace msgpack
diff --git a/silo/masstree/msgpack.hh b/silo/masstree/msgpack.hh
new file mode 100644 (file)
index 0000000..f4bc4dc
--- /dev/null
@@ -0,0 +1,692 @@
+#ifndef GSTORE_MSGPACK_HH
+#define GSTORE_MSGPACK_HH
+#include "json.hh"
+#include "local_vector.hh"
+#include "straccum.hh"
+#include <vector>
+namespace msgpack {
+using lcdf::Json;
+using lcdf::Str;
+using lcdf::String;
+using lcdf::StringAccum;
+
+namespace format {
+enum {
+    ffixuint = 0x00, nfixuint = 0x80,
+    ffixmap = 0x80, nfixmap = 0x10,
+    ffixarray = 0x90, nfixarray = 0x10,
+    ffixstr = 0xA0, nfixstr = 0x20,
+    fnull = 0xC0,
+    ffalse = 0xC2, ftrue = 0xC3,
+    fbin8 = 0xC4, fbin16 = 0xC5, fbin32 = 0xC6,
+    fext8 = 0xC7, fext16 = 0xC8, fext32 = 0xC9,
+    ffloat32 = 0xCA, ffloat64 = 0xCB,
+    fuint8 = 0xCC, fuint16 = 0xCD, fuint32 = 0xCE, fuint64 = 0xCF,
+    fint8 = 0xD0, fint16 = 0xD1, fint32 = 0xD2, fint64 = 0xD3,
+    ffixext1 = 0xD4, ffixext2 = 0xD5, ffixext4 = 0xD6,
+    ffixext8 = 0xD7, ffixext16 = 0xD8,
+    fstr8 = 0xD9, fstr16 = 0xDA, fstr32 = 0xDB,
+    farray16 = 0xDC, farray32 = 0xDD,
+    fmap16 = 0xDE, fmap32 = 0xDF,
+    ffixnegint = 0xE0, nfixnegint = 0x20,
+    nfixint = nfixuint + nfixnegint
+};
+
+inline bool in_range(uint8_t x, unsigned low, unsigned n) {
+    return (unsigned) x - low < n;
+}
+inline bool in_wrapped_range(uint8_t x, unsigned low, unsigned n) {
+    return (unsigned) (int8_t) x - low < n;
+}
+
+inline bool is_fixint(uint8_t x) {
+    return in_wrapped_range(x, -nfixnegint, nfixint);
+}
+inline bool is_null_or_bool(uint8_t x) {
+    return in_range(x, fnull, 4);
+}
+inline bool is_bool(uint8_t x) {
+    return in_range(x, ffalse, 2);
+}
+inline bool is_fixstr(uint8_t x) {
+    return in_range(x, ffixstr, nfixstr);
+}
+inline bool is_fixarray(uint8_t x) {
+    return in_range(x, ffixarray, nfixarray);
+}
+inline bool is_fixmap(uint8_t x) {
+    return in_range(x, ffixmap, nfixmap);
+}
+
+inline char* write_null(char* s) {
+    *s++ = fnull;
+    return s;
+}
+inline char* write_bool(char* s, bool x) {
+    *s++ = ffalse + x;
+    return s;
+}
+
+template <size_t s> struct sized_writer {};
+template <> struct sized_writer<4> {
+    static inline char* write_unsigned(char* s, uint32_t x) {
+        if (x < nfixuint)
+            *s++ = x;
+        else if (x < 256) {
+            *s++ = fuint8;
+            *s++ = x;
+        } else if (x < 65536) {
+            *s++ = fuint16;
+            s = write_in_net_order<uint16_t>(s, (uint16_t) x);
+        } else {
+            *s++ = fuint32;
+            s = write_in_net_order<uint32_t>(s, x);
+        }
+        return s;
+    }
+    static inline char* write_signed(char* s, int32_t x) {
+        if ((uint32_t) x + nfixnegint < nfixint)
+            *s++ = x;
+        else if ((uint32_t) x + 128 < 256) {
+            *s++ = fint8;
+            *s++ = x;
+        } else if ((uint32_t) x + 32768 < 65536) {
+            *s++ = fint16;
+            s = write_in_net_order<int16_t>(s, (int16_t) x);
+        } else {
+            *s++ = fint32;
+            s = write_in_net_order<int32_t>(s, x);
+        }
+        return s;
+    }
+};
+template <> struct sized_writer<8> {
+    static inline char* write_unsigned(char* s, uint64_t x) {
+        if (x < 4294967296ULL)
+            return sized_writer<4>::write_unsigned(s, (uint32_t) x);
+        else {
+            *s++ = fuint64;
+            return write_in_net_order<uint64_t>(s, x);
+        }
+    }
+    static inline char* write_signed(char* s, int64_t x) {
+        if (x + 2147483648ULL < 4294967296ULL)
+            return sized_writer<4>::write_signed(s, (int32_t) x);
+        else {
+            *s++ = fint64;
+            return write_in_net_order<int64_t>(s, x);
+        }
+    }
+};
+inline char* write_int(char* s, int x) {
+    return sized_writer<sizeof(x)>::write_signed(s, x);
+}
+inline char* write_int(char* s, unsigned x) {
+    return sized_writer<sizeof(x)>::write_unsigned(s, x);
+}
+inline char* write_int(char* s, long x) {
+    return sized_writer<sizeof(x)>::write_signed(s, x);
+}
+inline char* write_int(char* s, unsigned long x) {
+    return sized_writer<sizeof(x)>::write_unsigned(s, x);
+}
+#if HAVE_LONG_LONG && SIZEOF_LONG_LONG <= 8
+inline char* write_int(char* s, long long x) {
+    return sized_writer<sizeof(x)>::write_signed(s, x);
+}
+inline char* write_int(char* s, unsigned long long x) {
+    return sized_writer<sizeof(x)>::write_unsigned(s, x);
+}
+#endif
+inline char* write_wide_int64(char* s, uint64_t x) {
+    *s++ = fuint64;
+    return write_in_net_order<uint64_t>(s, x);
+}
+inline char* write_wide_int64(char* s, int64_t x) {
+    *s++ = fint64;
+    return write_in_net_order<int64_t>(s, x);
+}
+inline char* write_float(char* s, float x) {
+    *s++ = ffloat32;
+    return write_in_net_order<float>(s, x);
+}
+inline char* write_double(char* s, double x) {
+    *s++ = ffloat64;
+    return write_in_net_order<double>(s, x);
+}
+inline char* write_string(char* s, const char *data, int len) {
+    if (len < nfixstr)
+        *s++ = 0xA0 + len;
+    else if (len < 256) {
+        *s++ = fstr8;
+        *s++ = len;
+    } else if (len < 65536) {
+        *s++ = fstr16;
+        s = write_in_net_order<uint16_t>(s, (uint16_t) len);
+    } else {
+        *s++ = fstr32;
+        s = write_in_net_order<uint32_t>(s, len);
+    }
+    memcpy(s, data, len);
+    return s + len;
+}
+inline char* write_string(char* s, Str x) {
+    return write_string(s, x.data(), x.length());
+}
+template <typename T>
+inline char* write_string(char* s, const lcdf::String_base<T>& x) {
+    return write_string(s, x.data(), x.length());
+}
+inline char* write_array_header(char* s, uint32_t size) {
+    if (size < nfixarray) {
+        *s++ = ffixarray + size;
+        return s;
+    } else if (size < 65536) {
+        *s++ = farray16;
+        return write_in_net_order<uint16_t>(s, (uint16_t) size);
+    } else {
+        *s++ = farray32;
+        return write_in_net_order<uint32_t>(s, (uint32_t) size);
+    }
+}
+inline char* write_map_header(char* s, uint32_t size) {
+    if (size < nfixmap) {
+        *s++ = ffixmap + size;
+        return s;
+    } else if (size < 65536) {
+        *s++ = fmap16;
+        return write_in_net_order<uint16_t>(s, (uint16_t) size);
+    } else {
+        *s++ = fmap32;
+        return write_in_net_order<uint32_t>(s, (uint32_t) size);
+    }
+}
+} // namespace format
+
+struct array_t {
+    uint32_t size;
+    array_t(uint32_t s)
+        : size(s) {
+    }
+};
+
+inline array_t array(uint32_t size) {
+    return array_t(size);
+}
+
+struct object_t {
+    uint32_t size;
+    object_t(uint32_t s)
+        : size(s) {
+    }
+};
+
+inline object_t object(uint32_t size) {
+    return object_t(size);
+}
+
+template <typename T>
+class unparser {
+  public:
+    inline unparser(T& base)
+        : base_(base) {
+    }
+    template <typename X>
+    inline unparser(T& base, const X& x)
+        : base_(base) {
+        *this << x;
+    }
+
+    inline void clear() {
+        base_.clear();
+    }
+
+    inline unparser<T>& null() {
+        base_.append((char) format::fnull);
+        return *this;
+    }
+    inline unparser<T>& operator<<(const Json::null_t&) {
+        return null();
+    }
+    inline unparser<T>& operator<<(int x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_int(s, x));
+        return *this;
+    }
+    inline unparser<T>& operator<<(unsigned x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_int(s, x));
+        return *this;
+    }
+    inline unparser<T>& operator<<(long x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_int(s, x));
+        return *this;
+    }
+    inline unparser<T>& operator<<(unsigned long x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_int(s, x));
+        return *this;
+    }
+#if HAVE_LONG_LONG && SIZEOF_LONG_LONG <= 8
+    inline unparser<T>& operator<<(long long x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_int(s, x));
+        return *this;
+    }
+    inline unparser<T>& operator<<(unsigned long long x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_int(s, x));
+        return *this;
+    }
+#endif
+    inline unparser<T>& write_wide(int64_t x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_wide_int64(s, x));
+        return *this;
+    }
+    inline unparser<T>& write_wide(uint64_t x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_wide_int64(s, x));
+        return *this;
+    }
+    inline unparser<T>& operator<<(float x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_float(s, x));
+        return *this;
+    }
+    inline unparser<T>& operator<<(double x) {
+        char* s = base_.reserve(sizeof(x) + 1);
+        base_.set_end(format::write_double(s, x));
+        return *this;
+    }
+    inline unparser<T>& operator<<(Str x) {
+        char* s = base_.reserve(5 + x.length());
+        base_.set_end(format::write_string(s, x.data(), x.length()));
+        return *this;
+    }
+    template <typename X>
+    inline unparser<T>& operator<<(const lcdf::String_base<X>& x) {
+        char* s = base_.reserve(5 + x.length());
+        base_.set_end(format::write_string(s, x.data(), x.length()));
+        return *this;
+    }
+    inline unparser<T>& operator<<(array_t x) {
+        char* s = base_.reserve(5);
+        base_.set_end(format::write_array_header(s, x.size));
+        return *this;
+    }
+    inline unparser<T>& write_array_header(uint32_t size) {
+        char* s = base_.reserve(5);
+        base_.set_end(format::write_array_header(s, size));
+        return *this;
+    }
+    inline unparser<T>& operator<<(object_t x) {
+        char* s = base_.reserve(5);
+        base_.set_end(format::write_map_header(s, x.size));
+        return *this;
+    }
+    unparser<T>& operator<<(const Json& j);
+    template <typename X>
+    inline unparser<T>& write(const X& x) {
+        return *this << x;
+    }
+
+  private:
+    T& base_;
+};
+
+class streaming_parser {
+  public:
+    inline streaming_parser();
+    inline void reset();
+
+    inline bool empty() const;
+    inline bool done() const;
+    inline bool success() const;
+    inline bool error() const;
+
+    inline size_t consume(const char* first, size_t length,
+                          const String& str = String());
+    inline const char* consume(const char* first, const char* last,
+                               const String& str = String());
+    const uint8_t* consume(const uint8_t* first, const uint8_t* last,
+                           const String& str = String());
+
+    inline Json& result();
+    inline const Json& result() const;
+
+  private:
+    enum {
+        st_final = -2, st_error = -1, st_normal = 0, st_partial = 1,
+        st_string = 2
+    };
+    struct selem {
+        Json* jp;
+        int size;
+    };
+    int state_;
+    local_vector<selem, 2> stack_;
+    String str_;
+    Json json_;
+    Json jokey_;
+};
+
+class parser {
+  public:
+    explicit inline parser(const char* s)
+        : s_(reinterpret_cast<const unsigned char*>(s)), str_() {
+    }
+    explicit inline parser(const unsigned char* s)
+        : s_(s), str_() {
+    }
+    explicit inline parser(const String& str)
+        : s_(reinterpret_cast<const uint8_t*>(str.begin())), str_(str) {
+    }
+    inline const char* position() const {
+        return reinterpret_cast<const char*>(s_);
+    }
+
+    inline bool try_read_null() {
+        if (*s_ == format::fnull) {
+            ++s_;
+            return true;
+        } else
+            return false;
+    }
+    inline int read_tiny_int() {
+        assert(format::is_fixint(*s_));
+        return (int8_t) *s_++;
+    }
+    inline parser& read_tiny_int(int& x) {
+        x = read_tiny_int();
+        return *this;
+    }
+    template <typename T>
+    inline parser& read_int(T& x) {
+        if (format::is_fixint(*s_)) {
+            x = (int8_t) *s_;
+            ++s_;
+        } else {
+            assert((uint32_t) *s_ - format::fuint8 < 8);
+            hard_read_int(x);
+        }
+        return *this;
+    }
+    inline parser& operator>>(int& x) {
+        return read_int(x);
+    }
+    inline parser& operator>>(long& x) {
+        return read_int(x);
+    }
+    inline parser& operator>>(long long& x) {
+        return read_int(x);
+    }
+    inline parser& operator>>(unsigned& x) {
+        return read_int(x);
+    }
+    inline parser& operator>>(unsigned long& x) {
+        return read_int(x);
+    }
+    inline parser& operator>>(unsigned long long& x) {
+        return read_int(x);
+    }
+    inline parser& operator>>(bool& x) {
+        assert(format::is_bool(*s_));
+        x = *s_ - format::ffalse;
+        ++s_;
+        return *this;
+    }
+    inline parser& operator>>(double& x) {
+        assert(*s_ == format::ffloat64);
+        x = read_in_net_order<double>(s_ + 1);
+        s_ += 9;
+        return *this;
+    }
+    parser& operator>>(Str& x);
+    parser& operator>>(String& x);
+    inline parser& read_array_header(unsigned& size) {
+        if (format::is_fixarray(*s_)) {
+            size = *s_ - format::ffixarray;
+            s_ += 1;
+        } else if (*s_ == format::farray16) {
+            size = read_in_net_order<uint16_t>(s_ + 1);
+            s_ += 3;
+        } else {
+            assert(*s_ == format::farray32);
+            size = read_in_net_order<uint32_t>(s_ + 1);
+            s_ += 5;
+        }
+        return *this;
+    }
+    template <typename T> parser& operator>>(::std::vector<T>& x);
+    inline parser& operator>>(Json& j);
+
+    inline parser& skip_primitives(unsigned n) {
+        for (; n != 0; --n) {
+            if (format::is_fixint(*s_) || format::is_null_or_bool(*s_))
+                s_ += 1;
+            else if (format::is_fixstr(*s_))
+                s_ += 1 + (*s_ - format::ffixstr);
+            else if (*s_ == format::fuint8 || *s_ == format::fint8)
+                s_ += 2;
+            else if (*s_ == format::fuint16 || *s_ == format::fint16)
+                s_ += 3;
+            else if (*s_ == format::fuint32 || *s_ == format::fint32
+                     || *s_ == format::ffloat32)
+                s_ += 5;
+            else if (*s_ == format::fstr8 || *s_ == format::fbin8)
+                s_ += 2 + s_[1];
+            else if (*s_ == format::fstr16 || *s_ == format::fbin16)
+                s_ += 3 + read_in_net_order<uint16_t>(s_ + 1);
+            else if (*s_ == format::fstr32 || *s_ == format::fbin32)
+                s_ += 5 + read_in_net_order<uint32_t>(s_ + 1);
+        }
+        return *this;
+    }
+    inline parser& skip_primitive() {
+        return skip_primitives(1);
+    }
+    inline parser& skip_array_size() {
+        if (format::is_fixarray(*s_))
+            s_ += 1;
+        else if (*s_ == format::farray16)
+            s_ += 3;
+        else {
+            assert(*s_ == format::farray32);
+            s_ += 5;
+        }
+        return *this;
+    }
+  private:
+    const uint8_t* s_;
+    String str_;
+    template <typename T> void hard_read_int(T& x);
+};
+
+template <typename T>
+void parser::hard_read_int(T& x) {
+    switch (*s_) {
+    case format::fuint8:
+        x = s_[1];
+        s_ += 2;
+        break;
+    case format::fuint16:
+        x = read_in_net_order<uint16_t>(s_ + 1);
+        s_ += 3;
+        break;
+    case format::fuint32:
+        x = read_in_net_order<uint32_t>(s_ + 1);
+        s_ += 5;
+        break;
+    case format::fuint64:
+        x = read_in_net_order<uint64_t>(s_ + 1);
+        s_ += 9;
+        break;
+    case format::fint8:
+        x = (int8_t) s_[1];
+        s_ += 2;
+        break;
+    case format::fint16:
+        x = read_in_net_order<int16_t>(s_ + 1);
+        s_ += 3;
+        break;
+    case format::fint32:
+        x = read_in_net_order<int32_t>(s_ + 1);
+        s_ += 5;
+        break;
+    case format::fint64:
+        x = read_in_net_order<int64_t>(s_ + 1);
+        s_ += 9;
+        break;
+    }
+}
+
+template <typename T>
+unparser<T>& unparser<T>::operator<<(const Json& j) {
+    if (j.is_null())
+        base_.append(char(format::fnull));
+    else if (j.is_b())
+        base_.append(char(format::ffalse + j.as_b()));
+    else if (j.is_u()) {
+        char* x = base_.reserve(9);
+        base_.set_end(format::write_int(x, j.as_u()));
+    } else if (j.is_i()) {
+        char* x = base_.reserve(9);
+        base_.set_end(format::write_int(x, j.as_i()));
+    } else if (j.is_d()) {
+        char* x = base_.reserve(9);
+        base_.set_end(format::write_double(x, j.as_d()));
+    } else if (j.is_s()) {
+        char* x = base_.reserve(j.as_s().length() + 5);
+        base_.set_end(format::write_string(x, j.as_s()));
+    } else if (j.is_a()) {
+        char* x = base_.reserve(5);
+        base_.set_end(format::write_array_header(x, j.size()));
+        for (auto it = j.cabegin(); it != j.caend(); ++it)
+            *this << *it;
+    } else if (j.is_o()) {
+        char* x = base_.reserve(5);
+        base_.set_end(format::write_map_header(x, j.size()));
+        for (auto it = j.cobegin(); it != j.coend(); ++it) {
+            char* x = base_.reserve(it.key().length() + 5);
+            base_.set_end(format::write_string(x, it.key()));
+            *this << it.value();
+        }
+    } else
+        base_.append(char(format::fnull));
+    return *this;
+}
+
+inline String unparse(const Json& j) {
+    StringAccum sa;
+    unparser<StringAccum>(sa, j);
+    return sa.take_string();
+}
+template <typename T, typename X>
+inline T& unparse(T& s, const X& x) {
+    unparser<T>(s) << x;
+    return s;
+}
+template <typename T, typename X>
+inline T& unparse_wide(T& s, const X& x) {
+    unparser<T>(s).write_wide(x);
+    return s;
+}
+
+template <typename T>
+parser& parser::operator>>(::std::vector<T>& x) {
+    uint32_t sz;
+    if ((uint32_t) *s_ - format::ffixarray < format::nfixarray) {
+        sz = *s_ - format::ffixarray;
+        ++s_;
+    } else if (*s_ == format::farray16) {
+        sz = read_in_net_order<uint16_t>(s_ + 1);
+        s_ += 3;
+    } else {
+        assert(*s_ == format::farray32);
+        sz = read_in_net_order<uint32_t>(s_ + 1);
+        s_ += 5;
+    }
+    for (; sz != 0; --sz) {
+        x.push_back(T());
+        parse(x.back());
+    }
+    return *this;
+}
+
+inline streaming_parser::streaming_parser()
+    : state_(st_normal) {
+}
+
+inline void streaming_parser::reset() {
+    state_ = st_normal;
+    stack_.clear();
+}
+
+inline bool streaming_parser::empty() const {
+    return state_ == st_normal && stack_.empty();
+}
+
+inline bool streaming_parser::done() const {
+    return state_ < 0;
+}
+
+inline bool streaming_parser::success() const {
+    return state_ == st_final;
+}
+
+inline bool streaming_parser::error() const {
+    return state_ == st_error;
+}
+
+inline const char* streaming_parser::consume(const char* first,
+                                             const char* last,
+                                             const String& str) {
+    return reinterpret_cast<const char*>
+        (consume(reinterpret_cast<const uint8_t*>(first),
+                 reinterpret_cast<const uint8_t*>(last), str));
+}
+
+inline size_t streaming_parser::consume(const char* first, size_t length,
+                                        const String& str) {
+    const uint8_t* ufirst = reinterpret_cast<const uint8_t*>(first);
+    return consume(ufirst, ufirst + length, str) - ufirst;
+}
+
+inline Json& streaming_parser::result() {
+    return json_;
+}
+
+inline const Json& streaming_parser::result() const {
+    return json_;
+}
+
+inline parser& parser::operator>>(Json& j)  {
+    using std::swap;
+    streaming_parser sp;
+    while (!sp.done())
+        s_ = sp.consume(s_, s_ + 4096, str_);
+    if (sp.success())
+        swap(j, sp.result());
+    return *this;
+}
+
+inline Json parse(const char* first, const char* last) {
+    streaming_parser sp;
+    first = sp.consume(first, last, String());
+    if (sp.success())
+        return sp.result();
+    return Json();
+}
+
+inline Json parse(const String& str) {
+    streaming_parser sp;
+    sp.consume(str.begin(), str.end(), str);
+    if (sp.success())
+        return sp.result();
+    return Json();
+}
+
+} // namespace msgpack
+#endif
diff --git a/silo/masstree/msgpacktest.cc b/silo/masstree/msgpacktest.cc
new file mode 100644 (file)
index 0000000..bff1deb
--- /dev/null
@@ -0,0 +1,209 @@
+#include "msgpack.hh"
+using namespace lcdf;
+
+enum { status_ok, status_error, status_incomplete };
+
+__attribute__((noreturn))
+static void test_error(const char* file, int line,
+                       const char* data, int len,
+                       String division, String message) {
+    std::cerr << file << ":" << line << " ("
+              << String(data, data + len).printable() << ")"
+              << (division ? " " : "") << division << ": "
+              << message << "\n";
+    exit(1);
+}
+
+static void onetest(const char* file, int line,
+                    const char* data, int len,
+                    String division, const char* take, int expected_take,
+                    const char* unparse, int status,
+                    msgpack::streaming_parser& a) {
+    if (expected_take >= 0 && take != data + expected_take)
+        test_error(file, line, data, len, division, "accept took " + String(take - data) + " chars, expected " + String(expected_take));
+
+    if (status == status_ok && !a.success())
+        test_error(file, line, data, len, division, "accept not done");
+    if (status == status_error && !a.error())
+        test_error(file, line, data, len, division, "accept not error");
+    if (status == status_incomplete && a.done())
+        test_error(file, line, data, len, division, "accept not incomplete");
+
+    if (unparse && a.result().unparse() != unparse)
+        test_error(file, line, data, len, division, "result was " + a.result().unparse() + "\n\texpected " + String(unparse));
+}
+
+static void test(const char* file, int line,
+                 const char* data, int len, int expected_take,
+                 const char* unparse, int status = status_ok) {
+    assert(expected_take <= len);
+
+    msgpack::streaming_parser a;
+    const char* take;
+    take = a.consume(data, data + len);
+    onetest(file, line, data, len, "", take, expected_take, unparse, status, a);
+
+    if (len > 1) {
+        a.reset();
+        take = data;
+        while (take != data + len) {
+            const char* x = a.consume(take, take + 1);
+            if (x != take + (take < data + expected_take))
+                test_error(file, line, data, len, "by 1s", "accept took unusual amount after " + String(x - data));
+            ++take;
+        }
+        onetest(file, line, data, len, "by 1s", take, -1, unparse, status, a);
+    }
+}
+
+#define TEST(...) test(__FILE__, __LINE__, ## __VA_ARGS__)
+
+void check_correctness() {
+    TEST("\0", 1, 1, "0");
+    TEST("\xFF  ", 3, 1, "-1");
+    TEST("\xC0  ", 3, 1, "null");
+    TEST("\xC2  ", 3, 1, "false");
+    TEST("\xC3  ", 3, 1, "true");
+    TEST("\xD0\xEE", 2, 2, "-18");
+    TEST("\x81\xA7" "compact\xC3", 11, 10, "{\"compact\":true}");
+    TEST("\x81\x00\x81\xA7" "compact\xC3", 13, 12, "{\"0\":{\"compact\":true}}");
+    TEST("\x82\x00\x81\xA7" "compact\xC3\xA1" "a\xC2", 16, 15, "{\"0\":{\"compact\":true},\"a\":false}");
+    TEST("\x93\x00\x01\x02", 5, 4, "[0,1,2]");
+    TEST("\x90     ", 5, 1, "[]");
+    TEST("\xDC\x00\x00     ", 5, 3, "[]");
+    TEST("\224\002\322\000\001\242\321\262p|00356|1000000000\245?!?#*\225\001\322\000\001\242\322\242t|\242t}\332\000Rt|<user_id:5>|<time:10>|<poster_id:5> s|<user_id>|<poster_id> p|<poster_id>|<time>",
+         130, 32, "[2,107217,\"p|00356|1000000000\",\"?!?#*\"]", status_ok);
+    TEST("\xCF\x80\0\0\0\0\0\0\0", 9, 9, "9223372036854775808");
+
+    {
+        msgpack::streaming_parser a;
+        Json j = Json::array(0, 0, 0);
+        swap(j, a.result());
+        a.reset();
+        a.consume("\x91\xC2", 2);
+        assert(a.success() && a.result().unparse() == "[false]");
+        a.reset();
+        a.consume("\xC0", 1);
+        assert(a.success() && a.result().unparse() == "null");
+        a.reset();
+        a.consume("\x82\xA7" "compact\xC3\x00\x00", 12);
+        assert(a.success() && a.result().unparse() == "{\"compact\":true,\"0\":0}");
+        a.reset();
+        a.consume("\x82\xA7" "compact\xC3\x00\x00", 12);
+        assert(a.success() && a.result().unparse() == "{\"compact\":true,\"0\":0}");
+    }
+
+    {
+        StringAccum sa;
+        msgpack::unparser<StringAccum> up(sa);
+        up.clear();
+        up << -32;
+        assert(sa.take_string() == "\xE0");
+        up.clear();
+        up << -33;
+        assert(sa.take_string() == "\xD0\xDF");
+        up.clear();
+        up << 127;
+        assert(sa.take_string() == "\x7F");
+        up.clear();
+        up << 128;
+        assert(sa.take_string() == String("\xD1\x00\x80", 3));
+        up << -32768;
+        assert(sa.take_string() == String("\xD1\x80\x00", 3));
+        up << -32769;
+        assert(sa.take_string() == String("\xD2\xFF\xFF\x7F\xFF", 5));
+    }
+
+    {
+        StringAccum sa;
+        msgpack::unparser<StringAccum> up(sa);
+        up.clear();
+        up << msgpack::array(2) << Json((uint64_t) 1 << 63)
+           << Json((int64_t) 1 << 63);
+        String result = sa.take_string();
+        TEST(result.c_str(), result.length(), result.length(),
+             "[9223372036854775808,-9223372036854775808]");
+    }
+
+    std::cout << "All tests pass!\n";
+}
+
+Json __attribute__((noinline)) parse_json(const char* first, const char* last) {
+    return Json::parse(first, last);
+}
+
+Json __attribute__((noinline)) parse_json(const String& str) {
+    return Json::parse(str);
+}
+
+static const char sample_json[] = "{\"name\": \"Deborah Estrin\", \"email\": \"estrin@usc.edu\", \"affiliation\": \"University of Southern California\", \"roles\": [\"pc\"]}";
+
+static const char sample_msgpack[] = "\204\244name\256Deborah Estrin\245email\256estrin@usc.edu\253affiliation\331!University of Southern California\245roles\221\242pc";
+
+static int parse_json_loop_size = 10000000;
+
+void parse_json_loop_1() {
+    int total_size = 0;
+    const char* sample_json_end = sample_json + strlen(sample_json);
+    for (int i = 0; i != parse_json_loop_size; ++i) {
+        Json j = Json::parse(sample_json, sample_json_end);
+        total_size += j.size();
+    }
+    assert(total_size == 4 * parse_json_loop_size);
+}
+
+void parse_json_loop_2() {
+    int total_size = 0;
+    const char* sample_json_end = sample_json + strlen(sample_json);
+    for (int i = 0; i != parse_json_loop_size; ++i) {
+        Json j = Json::parse(String(sample_json, sample_json_end));
+        total_size += j.size();
+    }
+    assert(total_size == 4 * parse_json_loop_size);
+}
+
+void parse_json_loop_3() {
+    int total_size = 0;
+    String sample_json_str(sample_json);
+    for (int i = 0; i != parse_json_loop_size; ++i) {
+        Json j = Json::parse(sample_json_str);
+        total_size += j.size();
+    }
+    assert(total_size == 4 * parse_json_loop_size);
+}
+
+void parse_msgpack_loop_1() {
+    int total_size = 0;
+    const char* sample_msgpack_end = sample_msgpack + strlen(sample_msgpack);
+    for (int i = 0; i != parse_json_loop_size; ++i) {
+        Json j = msgpack::parse(sample_msgpack, sample_msgpack_end);
+        total_size += j.size();
+    }
+    assert(total_size == 4 * parse_json_loop_size);
+}
+
+void parse_msgpack_loop_2() {
+    int total_size = 0;
+    const char* sample_msgpack_end = sample_msgpack + strlen(sample_msgpack);
+    for (int i = 0; i != parse_json_loop_size; ++i) {
+        Json j = msgpack::parse(String(sample_msgpack, sample_msgpack_end));
+        total_size += j.size();
+    }
+    assert(total_size == 4 * parse_json_loop_size);
+}
+
+void parse_msgpack_loop_3() {
+    int total_size = 0;
+    String sample_msgpack_str(sample_msgpack);
+    for (int i = 0; i != parse_json_loop_size; ++i) {
+        Json j = msgpack::parse(sample_msgpack_str);
+        total_size += j.size();
+    }
+    assert(total_size == 4 * parse_json_loop_size);
+}
+
+int main(int argc, char** argv) {
+    (void) argc, (void) argv;
+
+    check_correctness();
+}
diff --git a/silo/masstree/mtclient.cc b/silo/masstree/mtclient.cc
new file mode 100644 (file)
index 0000000..a072164
--- /dev/null
@@ -0,0 +1,1702 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <string.h>
+#include <pthread.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <arpa/inet.h>
+#include <math.h>
+#include <fcntl.h>
+#include "kvstats.hh"
+#include "kvio.hh"
+#include "json.hh"
+#include "kvtest.hh"
+#include "mtclient.hh"
+#include "kvrandom.hh"
+#include "clp.h"
+
+const char *serverip = "127.0.0.1";
+
+typedef void (*get_async_cb)(struct child *c, struct async *a,
+                             bool has_val, const Str &val);
+typedef void (*put_async_cb)(struct child *c, struct async *a,
+                             int status);
+typedef void (*remove_async_cb)(struct child *c, struct async *a,
+                                int status);
+
+struct async {
+    int cmd; // Cmd_ constant
+    unsigned seq;
+    union {
+        get_async_cb get_fn;
+        put_async_cb put_fn;
+        remove_async_cb remove_fn;
+    };
+    char key[16]; // just first 16 bytes
+    char wanted[16]; // just first 16 bytes
+    int wantedlen;
+    int acked;
+};
+#define MAXWINDOW 512
+unsigned window = MAXWINDOW;
+
+struct child {
+    int s;
+    int udp; // 1 -> udp, 0 -> tcp
+    KVConn *conn;
+
+    struct async a[MAXWINDOW];
+
+    unsigned seq0_;
+    unsigned seq1_;
+    unsigned long long nsent_;
+    int childno;
+
+    inline void check_flush();
+};
+
+void checkasync(struct child *c, int force);
+
+inline void child::check_flush() {
+    if ((seq1_ & ((window - 1) >> 1)) == 0)
+        conn->flush();
+    while (seq1_ - seq0_ >= window)
+        checkasync(this, 1);
+}
+
+void aget(struct child *, const Str &key, const Str &wanted, get_async_cb fn);
+void aget(struct child *c, long ikey, long iwanted, get_async_cb fn);
+void aget_col(struct child *c, const Str& key, int col, const Str& wanted,
+              get_async_cb fn);
+int get(struct child *c, const Str &key, char *val, int max);
+
+void asyncgetcb(struct child *, struct async *a, bool, const Str &val);
+void asyncgetcb_int(struct child *, struct async *a, bool, const Str &val);
+
+void aput(struct child *c, const Str &key, const Str &val,
+          put_async_cb fn = 0, const Str &wanted = Str());
+void aput_col(struct child *c, const Str &key, int col, const Str &val,
+              put_async_cb fn = 0, const Str &wanted = Str());
+int put(struct child *c, const Str &key, const Str &val);
+void asyncputcb(struct child *, struct async *a, int status);
+
+void aremove(struct child *c, const Str &key, remove_async_cb fn);
+bool remove(struct child *c, const Str &key);
+
+void udp1(struct child *);
+void w1b(struct child *);
+void u1(struct child *);
+void over1(struct child *);
+void over2(struct child *);
+void rec1(struct child *);
+void rec2(struct child *);
+void cpa(struct child *);
+void cpb(struct child *);
+void cpc(struct child *);
+void cpd(struct child *);
+void volt1a(struct child *);
+void volt1b(struct child *);
+void volt2a(struct child *);
+void volt2b(struct child *);
+void scantest(struct child *);
+
+static int children = 1;
+static uint64_t nkeys = 0;
+static int prefixLen = 0;
+static int keylen = 0;
+static uint64_t limit = ~uint64_t(0);
+double duration = 10;
+double duration2 = 0;
+int udpflag = 0;
+int quiet = 0;
+int first_server_port = 2117;
+// Should all child processes connects to the same UDP PORT on server
+bool share_server_port = false;
+volatile bool timeout[2] = {false, false};
+int first_local_port = 0;
+const char *input = NULL;
+static int rsinit_part = 0;
+int kvtest_first_seed = 0;
+static int rscale_partsz = 0;
+static int getratio = -1;
+static int minkeyletter = '0';
+static int maxkeyletter = '9';
+
+
+struct kvtest_client {
+    kvtest_client(struct child& c)
+        : c_(&c) {
+    }
+    struct child* child() const {
+        return c_;
+    }
+    int id() const {
+        return c_->childno;
+    }
+    int nthreads() const {
+        return ::children;
+    }
+    char minkeyletter() const {
+        return ::minkeyletter;
+    }
+    char maxkeyletter() const {
+        return ::maxkeyletter;
+    }
+    void register_timeouts(int n) {
+        (void) n;
+    }
+    bool timeout(int which) const {
+        return ::timeout[which];
+    }
+    uint64_t limit() const {
+        return ::limit;
+    }
+    int getratio() const {
+        assert(::getratio >= 0);
+        return ::getratio;
+    }
+    uint64_t nkeys() const {
+        return ::nkeys;
+    }
+    int keylen() const {
+        return ::keylen;
+    }
+    int prefixLen() const {
+        return ::prefixLen;
+    }
+    double now() const {
+        return ::now();
+    }
+
+    void get(long ikey, Str *value) {
+        quick_istr key(ikey);
+        aget(c_, key.string(),
+             Str(reinterpret_cast<const char *>(&value), sizeof(value)),
+             asyncgetcb);
+    }
+    void get(const Str &key, int *ivalue) {
+        aget(c_, key,
+             Str(reinterpret_cast<const char *>(&ivalue), sizeof(ivalue)),
+             asyncgetcb_int);
+    }
+    bool get_sync(long ikey) {
+        char got[512];
+        quick_istr key(ikey);
+        return ::get(c_, key.string(), got, sizeof(got)) >= 0;
+    }
+    void get_check(long ikey, long iexpected) {
+        aget(c_, ikey, iexpected, 0);
+    }
+    void get_check(const char *key, const char *val) {
+        aget(c_, Str(key), Str(val), 0);
+    }
+    void get_check(const Str &key, const Str &val) {
+        aget(c_, key, val, 0);
+    }
+    void get_check_key8(long ikey, long iexpected) {
+        quick_istr key(ikey, 8), expected(iexpected);
+        aget(c_, key.string(), expected.string(), 0);
+    }
+    void get_check_key10(long ikey, long iexpected) {
+        quick_istr key(ikey, 10), expected(iexpected);
+        aget(c_, key.string(), expected.string(), 0);
+    }
+    void many_get_check(int, long [], long []) {
+        assert(0);
+    }
+    void get_col_check(const Str &key, int col, const Str &value) {
+        aget_col(c_, key, col, value, 0);
+    }
+    void get_col_check(long ikey, int col, long ivalue) {
+        quick_istr key(ikey), value(ivalue);
+        get_col_check(key.string(), col, value.string());
+    }
+    void get_col_check_key10(long ikey, int col, long ivalue) {
+        quick_istr key(ikey, 10), value(ivalue);
+        get_col_check(key.string(), col, value.string());
+    }
+    void get_check_sync(long ikey, long iexpected) {
+        char key[512], val[512], got[512];
+        sprintf(key, "%010ld", ikey);
+        sprintf(val, "%ld", iexpected);
+        memset(got, 0, sizeof(got));
+        ::get(c_, Str(key), got, sizeof(got));
+        if (strcmp(val, got)) {
+            fprintf(stderr, "key %s, expected %s, got %s\n", key, val, got);
+            always_assert(0);
+        }
+    }
+
+    void put(const Str &key, const Str &value) {
+        aput(c_, key, value);
+    }
+    void put(const Str &key, const Str &value, int *status) {
+        aput(c_, key, value,
+             asyncputcb,
+             Str(reinterpret_cast<const char *>(&status), sizeof(status)));
+    }
+    void put(const char *key, const char *value) {
+        aput(c_, Str(key), Str(value));
+    }
+    void put(const Str &key, long ivalue) {
+        quick_istr value(ivalue);
+        aput(c_, key, value.string());
+    }
+    void put(long ikey, long ivalue) {
+        quick_istr key(ikey), value(ivalue);
+        aput(c_, key.string(), value.string());
+    }
+    void put_key8(long ikey, long ivalue) {
+        quick_istr key(ikey, 8), value(ivalue);
+        aput(c_, key.string(), value.string());
+    }
+    void put_key10(long ikey, long ivalue) {
+        quick_istr key(ikey, 10), value(ivalue);
+        aput(c_, key.string(), value.string());
+    }
+    void put_col(const Str &key, int col, const Str &value) {
+        aput_col(c_, key, col, value);
+    }
+    void put_col(long ikey, int col, long ivalue) {
+        quick_istr key(ikey), value(ivalue);
+        put_col(key.string(), col, value.string());
+    }
+    void put_col_key10(long ikey, int col, long ivalue) {
+        quick_istr key(ikey, 10), value(ivalue);
+        put_col(key.string(), col, value.string());
+    }
+    void put_sync(long ikey, long ivalue) {
+        quick_istr key(ikey, 10), value(ivalue);
+        ::put(c_, key.string(), value.string());
+    }
+
+    void remove(const Str &key) {
+        aremove(c_, key, 0);
+    }
+    void remove(long ikey) {
+        quick_istr key(ikey);
+        remove(key.string());
+    }
+    bool remove_sync(long ikey) {
+        quick_istr key(ikey);
+        return ::remove(c_, key.string());
+    }
+
+    int ruscale_partsz() const {
+        return ::rscale_partsz;
+    }
+    int ruscale_init_part_no() const {
+        return ::rsinit_part;
+    }
+    long nseqkeys() const {
+        return 16 * ::rscale_partsz;
+    }
+    void wait_all() {
+        checkasync(c_, 2);
+    }
+    void puts_done() {
+    }
+    void rcu_quiesce() {
+    }
+    void notice(String s) {
+        if (!quiet) {
+            if (!s.empty() && s.back() == '\n')
+                s = s.substr(0, -1);
+            if (s.empty() || isspace((unsigned char) s[0]))
+                fprintf(stderr, "%d%.*s\n", c_->childno, s.length(), s.data());
+            else
+                fprintf(stderr, "%d %.*s\n", c_->childno, s.length(), s.data());
+        }
+    }
+    void notice(const char *fmt, ...) {
+        if (!quiet) {
+            va_list val;
+            va_start(val, fmt);
+            String x;
+            if (!*fmt || isspace((unsigned char) *fmt))
+                x = String(c_->childno) + fmt;
+            else
+                x = String(c_->childno) + String(" ") + fmt;
+            vfprintf(stderr, x.c_str(), val);
+            va_end(val);
+        }
+    }
+    const Json& report(const Json& x) {
+        return report_.merge(x);
+    }
+    void finish() {
+        if (!quiet) {
+            lcdf::StringAccum sa;
+            double dv;
+            if (report_.count("puts"))
+                sa << " total " << report_.get("puts");
+            if (report_.get("puts_per_sec", dv))
+                sa.snprintf(100, " %.0f put/s", dv);
+            if (report_.get("gets_per_sec", dv))
+                sa.snprintf(100, " %.0f get/s", dv);
+            if (!sa.empty())
+                notice(sa.take_string());
+        }
+        printf("%s\n", report_.unparse().c_str());
+    }
+    kvrandom_random rand;
+    struct child *c_;
+    Json report_;
+};
+
+
+#define TESTRUNNER_CLIENT_TYPE kvtest_client&
+#include "testrunner.hh"
+
+MAKE_TESTRUNNER(rw1, kvtest_rw1(client));
+MAKE_TESTRUNNER(rw2, kvtest_rw2(client));
+MAKE_TESTRUNNER(rw3, kvtest_rw3(client));
+MAKE_TESTRUNNER(rw4, kvtest_rw4(client));
+MAKE_TESTRUNNER(rw1fixed, kvtest_rw1fixed(client));
+MAKE_TESTRUNNER(rw16, kvtest_rw16(client));
+MAKE_TESTRUNNER(sync_rw1, kvtest_sync_rw1(client));
+MAKE_TESTRUNNER(r1, kvtest_r1_seed(client, kvtest_first_seed + client.id()));
+MAKE_TESTRUNNER(w1, kvtest_w1_seed(client, kvtest_first_seed + client.id()));
+MAKE_TESTRUNNER(w1b, w1b(client.child()));
+MAKE_TESTRUNNER(u1, u1(client.child()));
+MAKE_TESTRUNNER(wd1, kvtest_wd1(10000000, 1, client));
+MAKE_TESTRUNNER(wd1m1, kvtest_wd1(100000000, 1, client));
+MAKE_TESTRUNNER(wd1m2, kvtest_wd1(1000000000, 4, client));
+MAKE_TESTRUNNER(wd1check, kvtest_wd1_check(10000000, 1, client));
+MAKE_TESTRUNNER(wd1m1check, kvtest_wd1_check(100000000, 1, client));
+MAKE_TESTRUNNER(wd1m2check, kvtest_wd1_check(1000000000, 4, client));
+MAKE_TESTRUNNER(wd2, kvtest_wd2(client));
+MAKE_TESTRUNNER(wd2check, kvtest_wd2_check(client));
+MAKE_TESTRUNNER(tri1, kvtest_tri1(10000000, 1, client));
+MAKE_TESTRUNNER(tri1check, kvtest_tri1_check(10000000, 1, client));
+MAKE_TESTRUNNER(same, kvtest_same(client));
+MAKE_TESTRUNNER(wcol1, kvtest_wcol1at(client, client.id() % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(rcol1, kvtest_rcol1at(client, client.id() % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(wcol1o1, kvtest_wcol1at(client, (client.id() + 1) % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(rcol1o1, kvtest_rcol1at(client, (client.id() + 1) % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(wcol1o2, kvtest_wcol1at(client, (client.id() + 2) % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(rcol1o2, kvtest_rcol1at(client, (client.id() + 2) % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(over1, over1(client.child()));
+MAKE_TESTRUNNER(over2, over2(client.child()));
+MAKE_TESTRUNNER(rec1, rec1(client.child()));
+MAKE_TESTRUNNER(rec2, rec2(client.child()));
+MAKE_TESTRUNNER(cpa, cpa(client.child()));
+MAKE_TESTRUNNER(cpb, cpb(client.child()));
+MAKE_TESTRUNNER(cpc, cpc(client.child()));
+MAKE_TESTRUNNER(cpd, cpd(client.child()));
+MAKE_TESTRUNNER(volt1a, volt1a(client.child()));
+MAKE_TESTRUNNER(volt1b, volt1b(client.child()));
+MAKE_TESTRUNNER(volt2a, volt2a(client.child()));
+MAKE_TESTRUNNER(volt2b, volt2b(client.child()));
+MAKE_TESTRUNNER(scantest, scantest(client.child()));
+MAKE_TESTRUNNER(wscale, kvtest_wscale(client));
+MAKE_TESTRUNNER(ruscale_init, kvtest_ruscale_init(client));
+MAKE_TESTRUNNER(rscale, kvtest_rscale(client));
+MAKE_TESTRUNNER(uscale, kvtest_uscale(client));
+MAKE_TESTRUNNER(long_init, kvtest_long_init(client));
+MAKE_TESTRUNNER(long_go, kvtest_long_go(client));
+MAKE_TESTRUNNER(udp1, kvtest_udp1(client));
+
+void run_child(testrunner*, int childno);
+
+
+void
+usage()
+{
+  fprintf(stderr, "Usage: mtclient [-s serverip] [-w window] [--udp] "\
+          "[-j nchildren] [-d duration] [--ssp] [--flp first_local_port] "\
+          "[--fsp first_server_port] [-i json_input]\nTests:\n");
+  testrunner::print_names(stderr, 5);
+  exit(1);
+}
+
+void
+settimeout(int)
+{
+  if (!timeout[0]) {
+    timeout[0] = true;
+    if (duration2)
+        alarm((int) ceil(duration2));
+  } else
+    timeout[1] = true;
+}
+
+enum { clp_val_suffixdouble = Clp_ValFirstUser };
+enum { opt_threads = 1, opt_threads_deprecated, opt_duration, opt_duration2,
+       opt_window, opt_server, opt_first_server_port, opt_quiet, opt_udp,
+       opt_first_local_port, opt_share_server_port, opt_input,
+       opt_rsinit_part, opt_first_seed, opt_rscale_partsz, opt_keylen,
+       opt_limit, opt_prefix_len, opt_nkeys, opt_get_ratio, opt_minkeyletter,
+       opt_maxkeyletter, opt_nofork };
+static const Clp_Option options[] = {
+    { "threads", 'j', opt_threads, Clp_ValInt, 0 },
+    { 0, 'n', opt_threads_deprecated, Clp_ValInt, 0 },
+    { "duration", 'd', opt_duration, Clp_ValDouble, 0 },
+    { "duration2", 0, opt_duration2, Clp_ValDouble, 0 },
+    { "d2", 0, opt_duration2, Clp_ValDouble, 0 },
+    { "window", 'w', opt_window, Clp_ValUnsigned, 0 },
+    { "server-ip", 's', opt_server, Clp_ValString, 0 },
+    { "first-server-port", 0, opt_first_server_port, Clp_ValInt, 0 },
+    { "fsp", 0, opt_first_server_port, Clp_ValInt, 0 },
+    { "quiet", 'q', opt_quiet, 0, Clp_Negate },
+    { "udp", 'u', opt_udp, 0, Clp_Negate },
+    { "first-local-port", 0, opt_first_local_port, Clp_ValInt, 0 },
+    { "flp", 0, opt_first_local_port, Clp_ValInt, 0 },
+    { "share-server-port", 0, opt_share_server_port, 0, Clp_Negate },
+    { "ssp", 0, opt_share_server_port, 0, Clp_Negate },
+    { "input", 'i', opt_input, Clp_ValString, 0 },
+    { "rsinit_part", 0, opt_rsinit_part, Clp_ValInt, 0 },
+    { "first_seed", 0, opt_first_seed, Clp_ValInt, 0 },
+    { "rscale_partsz", 0, opt_rscale_partsz, Clp_ValInt, 0 },
+    { "keylen", 0, opt_keylen, Clp_ValInt, 0 },
+    { "limit", 'l', opt_limit, clp_val_suffixdouble, 0 },
+    { "prefixLen", 0, opt_prefix_len, Clp_ValInt, 0 },
+    { "nkeys", 0, opt_nkeys, Clp_ValInt, 0 },
+    { "getratio", 0, opt_get_ratio, Clp_ValInt, 0 },
+    { "minkeyletter", 0, opt_minkeyletter, Clp_ValString, 0 },
+    { "maxkeyletter", 0, opt_maxkeyletter, Clp_ValString, 0 },
+    { "no-fork", 0, opt_nofork, 0, 0 }
+};
+
+int
+main(int argc, char *argv[])
+{
+  int i, pid, status;
+  testrunner* test = 0;
+  int pipes[512];
+  int dofork = 1;
+
+  Clp_Parser *clp = Clp_NewParser(argc, argv, (int) arraysize(options), options);
+  Clp_AddType(clp, clp_val_suffixdouble, Clp_DisallowOptions, clp_parse_suffixdouble, 0);
+  int opt;
+  while ((opt = Clp_Next(clp)) != Clp_Done) {
+      switch (opt) {
+      case opt_threads:
+          children = clp->val.i;
+          break;
+      case opt_threads_deprecated:
+          Clp_OptionError(clp, "%<%O%> is deprecated, use %<-j%>");
+          children = clp->val.i;
+          break;
+      case opt_duration:
+          duration = clp->val.d;
+          break;
+      case opt_duration2:
+          duration2 = clp->val.d;
+          break;
+      case opt_window:
+          window = clp->val.u;
+          always_assert(window <= MAXWINDOW);
+          always_assert((window & (window - 1)) == 0); // power of 2
+          break;
+      case opt_server:
+          serverip = clp->vstr;
+          break;
+      case opt_first_server_port:
+          first_server_port = clp->val.i;
+          break;
+      case opt_quiet:
+          quiet = !clp->negated;
+          break;
+      case opt_udp:
+          udpflag = !clp->negated;
+          break;
+      case opt_first_local_port:
+          first_local_port = clp->val.i;
+          break;
+      case opt_share_server_port:
+          share_server_port = !clp->negated;
+          break;
+      case opt_input:
+          input = clp->vstr;
+          break;
+      case opt_rsinit_part:
+          rsinit_part = clp->val.i;
+          break;
+      case opt_first_seed:
+          kvtest_first_seed = clp->val.i;
+          break;
+      case opt_rscale_partsz:
+          rscale_partsz = clp->val.i;
+          break;
+      case opt_keylen:
+          keylen = clp->val.i;
+          break;
+      case opt_limit:
+          limit = (uint64_t) clp->val.d;
+          break;
+      case opt_prefix_len:
+          prefixLen = clp->val.i;
+          break;
+      case opt_nkeys:
+          nkeys = clp->val.i;
+          break;
+      case opt_get_ratio:
+          getratio = clp->val.i;
+          break;
+      case opt_minkeyletter:
+          minkeyletter = clp->vstr[0];
+          break;
+      case opt_maxkeyletter:
+          maxkeyletter = clp->vstr[0];
+          break;
+      case opt_nofork:
+          dofork = !clp->negated;
+          break;
+      case Clp_NotOption:
+          test = testrunner::find(clp->vstr);
+          if (!test)
+              usage();
+          break;
+      case Clp_BadOption:
+          usage();
+          break;
+      }
+  }
+  if(children < 1 || (children != 1 && !dofork))
+    usage();
+  if (!test)
+      test = testrunner::first();
+
+  printf("%s, w %d, test %s, children %d\n",
+         udpflag ? "udp" : "tcp", window,
+         test->name().c_str(), children);
+
+  fflush(stdout);
+
+  if (dofork) {
+      for(i = 0; i < children; i++){
+          int ptmp[2];
+          int r = pipe(ptmp);
+          always_assert(r == 0);
+          pid = fork();
+          if(pid < 0){
+              perror("fork");
+              exit(1);
+          }
+          if(pid == 0){
+              close(ptmp[0]);
+              dup2(ptmp[1], 1);
+              close(ptmp[1]);
+              signal(SIGALRM, settimeout);
+              alarm((int) ceil(duration));
+              run_child(test, i);
+              exit(0);
+          }
+          pipes[i] = ptmp[0];
+          close(ptmp[1]);
+      }
+      for(i = 0; i < children; i++){
+          if(wait(&status) <= 0){
+              perror("wait");
+              exit(1);
+          }
+          if (WIFSIGNALED(status))
+              fprintf(stderr, "child %d died by signal %d\n", i, WTERMSIG(status));
+      }
+  } else {
+      int ptmp[2];
+      int r = pipe(ptmp);
+      always_assert(r == 0);
+      pipes[0] = ptmp[0];
+      int stdout_fd = dup(STDOUT_FILENO);
+      always_assert(stdout_fd > 0);
+      r = dup2(ptmp[1], STDOUT_FILENO);
+      always_assert(r >= 0);
+      close(ptmp[1]);
+      signal(SIGALRM, settimeout);
+      alarm((int) ceil(duration));
+      run_child(test, 0);
+      fflush(stdout);
+      r = dup2(stdout_fd, STDOUT_FILENO);
+      always_assert(r >= 0);
+      close(stdout_fd);
+  }
+
+  long long total = 0;
+  kvstats puts, gets, scans, puts_per_sec, gets_per_sec, scans_per_sec;
+  for(i = 0; i < children; i++){
+    char buf[2048];
+    int cc = read(pipes[i], buf, sizeof(buf)-1);
+    assert(cc > 0);
+    buf[cc] = 0;
+    printf("%s", buf);
+    Json bufj = Json::parse(buf, buf + cc);
+    long long iv;
+    double dv;
+    if (bufj.to_i(iv))
+        total += iv;
+    else if (bufj.is_object()) {
+        if (bufj.get("ops", iv)
+            || bufj.get("total", iv)
+            || bufj.get("count", iv))
+            total += iv;
+        if (bufj.get("puts", iv))
+            puts.add(iv);
+        if (bufj.get("gets", iv))
+            gets.add(iv);
+        if (bufj.get("scans", iv))
+            scans.add(iv);
+        if (bufj.get("puts_per_sec", dv))
+            puts_per_sec.add(dv);
+        if (bufj.get("gets_per_sec", dv))
+            gets_per_sec.add(dv);
+        if (bufj.get("scans_per_sec", dv))
+            scans_per_sec.add(dv);
+    }
+  }
+
+  printf("total %lld\n", total);
+  puts.print_report("puts");
+  gets.print_report("gets");
+  scans.print_report("scans");
+  puts_per_sec.print_report("puts/s");
+  gets_per_sec.print_report("gets/s");
+  scans_per_sec.print_report("scans/s");
+
+  exit(0);
+}
+
+void
+run_child(testrunner* test, int childno)
+{
+  struct sockaddr_in sin;
+  int ret, yes = 1;
+  struct child c;
+
+  bzero(&c, sizeof(c));
+  c.childno = childno;
+
+  if(udpflag){
+    c.udp = 1;
+    c.s = socket(AF_INET, SOCK_DGRAM, 0);
+  } else {
+    c.s = socket(AF_INET, SOCK_STREAM, 0);
+  }
+  if (first_local_port) {
+    bzero(&sin, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(first_local_port + (childno % 48));
+    ret = ::bind(c.s, (struct sockaddr *) &sin, sizeof(sin));
+    if (ret < 0) {
+      perror("bind");
+      exit(1);
+    }
+  }
+
+  assert(c.s >= 0);
+  setsockopt(c.s, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
+
+  bzero(&sin, sizeof(sin));
+  sin.sin_family = AF_INET;
+  if (udpflag && !share_server_port)
+    sin.sin_port = htons(first_server_port + (childno % 48));
+  else
+    sin.sin_port = htons(first_server_port);
+  sin.sin_addr.s_addr = inet_addr(serverip);
+  ret = connect(c.s, (struct sockaddr *) &sin, sizeof(sin));
+  if(ret < 0){
+    perror("connect");
+    exit(1);
+  }
+
+  c.conn = new KVConn(c.s, !udpflag);
+  kvtest_client client(c);
+
+  test->run(client);
+
+  checkasync(&c, 2);
+
+  delete c.conn;
+  close(c.s);
+}
+
+void KVConn::hard_check(int tryhard) {
+    masstree_precondition(inbufpos_ == inbuflen_);
+    if (parser_.empty()) {
+        inbufpos_ = inbuflen_ = 0;
+        for (auto x : oldinbuf_)
+            delete[] x;
+        oldinbuf_.clear();
+    } else if (inbufpos_ == inbufsz) {
+        oldinbuf_.push_back(inbuf_);
+        inbuf_ = new char[inbufsz];
+        inbufpos_ = inbuflen_ = 0;
+    }
+    if (tryhard == 1) {
+        fd_set rfds;
+        FD_ZERO(&rfds);
+        FD_SET(infd_, &rfds);
+        struct timeval tv = {0, 0};
+        if (select(infd_ + 1, &rfds, NULL, NULL, &tv) <= 0)
+            return;
+    } else
+        kvflush(out_);
+
+    ssize_t r = read(infd_, inbuf_ + inbufpos_, inbufsz - inbufpos_);
+    if (r != -1)
+        inbuflen_ += r;
+}
+
+int
+get(struct child *c, const Str &key, char *val, int max)
+{
+    assert(c->seq0_ == c->seq1_);
+
+    unsigned sseq = c->seq1_;
+    ++c->seq1_;
+    ++c->nsent_;
+
+    c->conn->sendgetwhole(key, sseq);
+    c->conn->flush();
+
+    const Json& result = c->conn->receive();
+    always_assert(result && result[0] == sseq);
+    ++c->seq0_;
+    if (!result[2])
+        return -1;
+    always_assert(result.size() == 3 && result[2].is_s()
+                  && result[2].as_s().length() <= max);
+    memcpy(val, result[2].as_s().data(), result[2].as_s().length());
+    return result[2].as_s().length();
+}
+
+// builtin aget callback: no check
+void
+nocheck(struct child *, struct async *, bool, const Str &)
+{
+}
+
+// builtin aget callback: store string
+void
+asyncgetcb(struct child *, struct async *a, bool, const Str &val)
+{
+    Str *sptr;
+    assert(a->wantedlen == sizeof(Str *));
+    memcpy(&sptr, a->wanted, sizeof(Str *));
+    sptr->len = std::min(sptr->len, val.len);
+    memcpy(const_cast<char *>(sptr->s), val.s, sptr->len);
+}
+
+// builtin aget callback: store string
+void
+asyncgetcb_int(struct child *, struct async *a, bool, const Str &val)
+{
+    int *vptr;
+    assert(a->wantedlen == sizeof(int *));
+    memcpy(&vptr, a->wanted, sizeof(int *));
+    long x = 0;
+    if (val.len <= 0)
+        x = -1;
+    else
+        for (int i = 0; i < val.len; ++i)
+            if (val.s[i] >= '0' && val.s[i] <= '9')
+                x = (x * 10) + (val.s[i] - '0');
+            else {
+                x = -1;
+                break;
+            }
+    *vptr = x;
+}
+
+// default aget callback: check val against wanted
+void
+defaultget(struct child *, struct async *a, bool have_val, const Str &val)
+{
+    // check that we got the expected value
+    int wanted_avail = std::min(a->wantedlen, int(sizeof(a->wanted)));
+    if (!have_val
+        || a->wantedlen != val.len
+        || memcmp(val.s, a->wanted, wanted_avail) != 0)
+        fprintf(stderr, "oops wanted %.*s(%d) got %.*s(%d)\n",
+                wanted_avail, a->wanted, a->wantedlen, val.len, val.s, val.len);
+    else {
+        always_assert(a->wantedlen == val.len);
+        always_assert(memcmp(val.s, a->wanted, wanted_avail) == 0);
+    }
+}
+
+// builtin aput/aremove callback: store status
+void
+asyncputcb(struct child *, struct async *a, int status)
+{
+    int *sptr;
+    assert(a->wantedlen == sizeof(int *));
+    memcpy(&sptr, a->wanted, sizeof(int *));
+    *sptr = status;
+}
+
+// process any waiting replies to aget() and aput().
+// force=0 means non-blocking check if anything waiting on socket.
+// force=1 means wait for at least one reply.
+// force=2 means wait for all pending (nw-nr) replies.
+void
+checkasync(struct child *c, int force)
+{
+    while (c->seq0_ != c->seq1_) {
+        if (force)
+            c->conn->flush();
+        if (c->conn->check(force ? 2 : 1) > 0) {
+            const Json& result = c->conn->receive();
+            always_assert(result);
+
+            // is rseq in the nr..nw window?
+            // replies might arrive out of order if UDP
+            unsigned rseq = result[0].as_i();
+            always_assert(rseq - c->seq0_ < c->seq1_ - c->seq0_);
+            struct async *a = &c->a[rseq & (window - 1)];
+            always_assert(a->seq == rseq);
+
+            // advance the nr..nw window
+            always_assert(a->acked == 0);
+            a->acked = 1;
+            while (c->seq0_ != c->seq1_ && c->a[c->seq0_ & (window - 1)].acked)
+                ++c->seq0_;
+
+            // might have been the last free slot,
+            // don't want to re-use it underfoot.
+            struct async tmpa = *a;
+
+            if(tmpa.cmd == Cmd_Get){
+                // this is a reply to a get
+                String s = result.size() > 2 ? result[2].as_s() : String();
+                if (tmpa.get_fn)
+                    (tmpa.get_fn)(c, &tmpa, result.size() > 2, s);
+            } else if (tmpa.cmd == Cmd_Put || tmpa.cmd == Cmd_Replace) {
+                // this is a reply to a put
+                if (tmpa.put_fn)
+                    (tmpa.put_fn)(c, &tmpa, result[2].as_i());
+            } else if(tmpa.cmd == Cmd_Scan){
+                // this is a reply to a scan
+                always_assert((result.size() - 2) / 2 <= tmpa.wantedlen);
+            } else if (tmpa.cmd == Cmd_Remove) {
+                // this is a reply to a remove
+                if (tmpa.remove_fn)
+                    (tmpa.remove_fn)(c, &tmpa, result[2].as_i());
+            } else {
+                always_assert(0);
+            }
+
+            if (force < 2)
+                force = 0;
+        } else if (!force)
+            break;
+    }
+}
+
+// async get, checkasync() will eventually check reply
+// against wanted.
+void
+aget(struct child *c, const Str &key, const Str &wanted, get_async_cb fn)
+{
+    c->check_flush();
+
+    c->conn->sendgetwhole(key, c->seq1_);
+    if (c->udp)
+        c->conn->flush();
+
+    struct async *a = &c->a[c->seq1_ & (window - 1)];
+    a->cmd = Cmd_Get;
+    a->seq = c->seq1_;
+    a->get_fn = (fn ? fn : defaultget);
+    assert(key.len < int(sizeof(a->key)));
+    memcpy(a->key, key.s, key.len);
+    a->key[key.len] = 0;
+    a->wantedlen = wanted.len;
+    int wantedavail = std::min(wanted.len, int(sizeof(a->wanted)));
+    memcpy(a->wanted, wanted.s, wantedavail);
+    a->acked = 0;
+
+    ++c->seq1_;
+    ++c->nsent_;
+}
+
+void aget_col(struct child *c, const Str& key, int col, const Str& wanted,
+              get_async_cb fn)
+{
+    c->check_flush();
+
+    c->conn->sendgetcol(key, col, c->seq1_);
+    if (c->udp)
+        c->conn->flush();
+
+    struct async *a = &c->a[c->seq1_ & (window - 1)];
+    a->cmd = Cmd_Get;
+    a->seq = c->seq1_;
+    a->get_fn = (fn ? fn : defaultget);
+    assert(key.len < int(sizeof(a->key)));
+    memcpy(a->key, key.s, key.len);
+    a->key[key.len] = 0;
+    a->wantedlen = wanted.len;
+    int wantedavail = std::min(wanted.len, int(sizeof(a->wanted)));
+    memcpy(a->wanted, wanted.s, wantedavail);
+    a->acked = 0;
+
+    ++c->seq1_;
+    ++c->nsent_;
+}
+
+void
+aget(struct child *c, long ikey, long iwanted, get_async_cb fn)
+{
+    quick_istr key(ikey), wanted(iwanted);
+    aget(c, key.string(), wanted.string(), fn);
+}
+
+int
+put(struct child *c, const Str &key, const Str &val)
+{
+    always_assert(c->seq0_ == c->seq1_);
+
+    unsigned int sseq = c->seq1_;
+    c->conn->sendputwhole(key, val, sseq);
+    c->conn->flush();
+
+    const Json& result = c->conn->receive();
+    always_assert(result && result[0] == sseq);
+
+    ++c->seq0_;
+    ++c->seq1_;
+    ++c->nsent_;
+    return 0;
+}
+
+void
+aput(struct child *c, const Str &key, const Str &val,
+     put_async_cb fn, const Str &wanted)
+{
+    c->check_flush();
+
+    c->conn->sendputwhole(key, val, c->seq1_);
+    if (c->udp)
+        c->conn->flush();
+
+    struct async *a = &c->a[c->seq1_ & (window - 1)];
+    a->cmd = Cmd_Put;
+    a->seq = c->seq1_;
+    assert(key.len < int(sizeof(a->key)) - 1);
+    memcpy(a->key, key.s, key.len);
+    a->key[key.len] = 0;
+    a->put_fn = fn;
+    if (fn) {
+        assert(wanted.len <= int(sizeof(a->wanted)));
+        a->wantedlen = wanted.len;
+        memcpy(a->wanted, wanted.s, wanted.len);
+    } else {
+        a->wantedlen = -1;
+        a->wanted[0] = 0;
+    }
+    a->acked = 0;
+
+    ++c->seq1_;
+    ++c->nsent_;
+}
+
+void aput_col(struct child *c, const Str &key, int col, const Str &val,
+              put_async_cb fn, const Str &wanted)
+{
+    c->check_flush();
+
+    c->conn->sendputcol(key, col, val, c->seq1_);
+    if (c->udp)
+        c->conn->flush();
+
+    struct async *a = &c->a[c->seq1_ & (window - 1)];
+    a->cmd = Cmd_Put;
+    a->seq = c->seq1_;
+    assert(key.len < int(sizeof(a->key)) - 1);
+    memcpy(a->key, key.s, key.len);
+    a->key[key.len] = 0;
+    a->put_fn = fn;
+    if (fn) {
+        assert(wanted.len <= int(sizeof(a->wanted)));
+        a->wantedlen = wanted.len;
+        memcpy(a->wanted, wanted.s, wanted.len);
+    } else {
+        a->wantedlen = -1;
+        a->wanted[0] = 0;
+    }
+    a->acked = 0;
+
+    ++c->seq1_;
+    ++c->nsent_;
+}
+
+bool remove(struct child *c, const Str &key)
+{
+    always_assert(c->seq0_ == c->seq1_);
+
+    unsigned int sseq = c->seq1_;
+    c->conn->sendremove(key, sseq);
+    c->conn->flush();
+
+    const Json& result = c->conn->receive();
+    always_assert(result && result[0] == sseq);
+
+    ++c->seq0_;
+    ++c->seq1_;
+    ++c->nsent_;
+    return result[2].to_b();
+}
+
+void
+aremove(struct child *c, const Str &key, remove_async_cb fn)
+{
+    c->check_flush();
+
+    c->conn->sendremove(key, c->seq1_);
+    if (c->udp)
+        c->conn->flush();
+
+    struct async *a = &c->a[c->seq1_ & (window - 1)];
+    a->cmd = Cmd_Remove;
+    a->seq = c->seq1_;
+    assert(key.len < int(sizeof(a->key)) - 1);
+    memcpy(a->key, key.s, key.len);
+    a->key[key.len] = 0;
+    a->acked = 0;
+    a->remove_fn = fn;
+
+    ++c->seq1_;
+    ++c->nsent_;
+}
+
+int
+xcompar(const void *xa, const void *xb)
+{
+  long *a = (long *) xa;
+  long *b = (long *) xb;
+  if(*a == *b)
+    return 0;
+  if(*a < *b)
+    return -1;
+  return 1;
+}
+
+// like w1, but in a binary-tree-like order that
+// produces a balanced 3-wide tree.
+void
+w1b(struct child *c)
+{
+  int n;
+  if (limit == ~(uint64_t) 0)
+      n = 4000000;
+  else
+      n = std::min(limit, (uint64_t) INT_MAX);
+  long *a = (long *) malloc(sizeof(long) * n);
+  always_assert(a);
+  char *done = (char *) malloc(n);
+
+  srandom(kvtest_first_seed + c->childno);
+
+  // insert in an order which causes 3-wide
+  // to be balanced
+
+  for(int i = 0; i < n; i++){
+    a[i] = random();
+    done[i] = 0;
+  }
+
+  qsort(a, n, sizeof(a[0]), xcompar);
+  always_assert(a[0] <= a[1] && a[1] <= a[2] && a[2] <= a[3]);
+
+  double t0 = now(), t1;
+
+  for(int stride = n / 2; stride > 0; stride /= 2){
+    for(int i = stride; i < n; i += stride){
+      if(done[i] == 0){
+        done[i] = 1;
+        char key[512], val[512];
+        sprintf(key, "%010ld", a[i]);
+        sprintf(val, "%ld", a[i] + 1);
+        aput(c, Str(key), Str(val));
+      }
+    }
+  }
+  for(int i = 0; i < n; i++){
+    if(done[i] == 0){
+      done[i] = 1;
+      char key[512], val[512];
+      sprintf(key, "%010ld", a[i]);
+      sprintf(val, "%ld", a[i] + 1);
+      aput(c, Str(key), Str(val));
+    }
+  }
+
+  checkasync(c, 2);
+  t1 = now();
+
+  free(done);
+  free(a);
+  Json result = Json().set("total", (long) (n / (t1 - t0)))
+    .set("puts", n)
+    .set("puts_per_sec", n / (t1 - t0));
+  printf("%s\n", result.unparse().c_str());
+}
+
+// update random keys from a set of 10 million.
+// maybe best to run it twice, first time to
+// populate the database.
+void
+u1(struct child *c)
+{
+  int i, n;
+  double t0 = now();
+
+  srandom(kvtest_first_seed + c->childno);
+
+  for(i = 0; i < 10000000; i++){
+    char key[512], val[512];
+    long x = random() % 10000000;
+    sprintf(key, "%ld", x);
+    sprintf(val, "%ld", x + 1);
+    aput(c, Str(key), Str(val));
+  }
+  n = i;
+
+  checkasync(c, 2);
+
+  double t1 = now();
+  Json result = Json().set("total", (long) (n / (t1 - t0)))
+    .set("puts", n)
+    .set("puts_per_sec", n / (t1 - t0));
+  printf("%s\n", result.unparse().c_str());
+}
+
+#define CPN 10000000
+
+void
+cpa(struct child *c)
+{
+  int i, n;
+  double t0 = now();
+
+  srandom(kvtest_first_seed + c->childno);
+
+  for(i = 0; i < CPN; i++){
+    char key[512], val[512];
+    long x = random();
+    sprintf(key, "%ld", x);
+    sprintf(val, "%ld", x + 1);
+    aput(c, Str(key), Str(val));
+  }
+  n = i;
+
+  checkasync(c, 2);
+
+  double t1 = now();
+  Json result = Json().set("total", (long) (n / (t1 - t0)))
+    .set("puts", n)
+    .set("puts_per_sec", n / (t1 - t0));
+  printf("%s\n", result.unparse().c_str());
+
+}
+
+void
+cpc(struct child *c)
+{
+  int i, n;
+  double t0 = now();
+
+  srandom(kvtest_first_seed + c->childno);
+
+  for(i = 0; !timeout[0]; i++){
+    char key[512], val[512];
+    if (i % CPN == 0)
+      srandom(kvtest_first_seed + c->childno);
+    long x = random();
+    sprintf(key, "%ld", x);
+    sprintf(val, "%ld", x + 1);
+    aget(c, Str(key), Str(val), NULL);
+  }
+  n = i;
+
+  checkasync(c, 2);
+
+  double t1 = now();
+  Json result = Json().set("total", (long) (n / (t1 - t0)))
+    .set("gets", n)
+    .set("gets_per_sec", n / (t1 - t0));
+  printf("%s\n", result.unparse().c_str());
+}
+
+void
+cpd(struct child *c)
+{
+  int i, n;
+  double t0 = now();
+
+  srandom(kvtest_first_seed + c->childno);
+
+  for(i = 0; !timeout[0]; i++){
+    char key[512], val[512];
+    if (i % CPN == 0)
+      srandom(kvtest_first_seed + c->childno);
+    long x = random();
+    sprintf(key, "%ld", x);
+    sprintf(val, "%ld", x + 1);
+    aput(c, Str(key), Str(val));
+  }
+  n = i;
+
+  checkasync(c, 2);
+
+  double t1 = now();
+  Json result = Json().set("total", (long) (n / (t1 - t0)))
+    .set("puts", n)
+    .set("puts_per_sec", n / (t1 - t0));
+  printf("%s\n", result.unparse().c_str());
+}
+
+// multiple threads simultaneously update the same key.
+// keep track of the winning value.
+// use over2 to make sure it's the same after a crash/restart.
+void
+over1(struct child *c)
+{
+  int ret, iter;
+
+  srandom(kvtest_first_seed + c->childno);
+
+  iter = 0;
+  while(!timeout[0]){
+    char key1[64], key2[64], val1[64], val2[64];
+    time_t xt = time(0);
+    while(xt == time(0))
+      ;
+    sprintf(key1, "%d", iter);
+    sprintf(val1, "%ld", random());
+    put(c, Str(key1), Str(val1));
+    napms(500);
+    ret = get(c, Str(key1), val2, sizeof(val2));
+    always_assert(ret > 0);
+    sprintf(key2, "%d-%d", iter, c->childno);
+    put(c, Str(key2), Str(val2));
+    if(c->childno == 0)
+      printf("%d: %s\n", iter, val2);
+    iter++;
+  }
+  checkasync(c, 2);
+  printf("0\n");
+}
+
+// check each round of over1()
+void
+over2(struct child *c)
+{
+  int iter;
+
+  for(iter = 0; ; iter++){
+    char key1[64], key2[64], val1[64], val2[64];
+    int ret;
+    sprintf(key1, "%d", iter);
+    ret = get(c, Str(key1), val1, sizeof(val1));
+    if(ret == -1)
+      break;
+    sprintf(key2, "%d-%d", iter, c->childno);
+    ret = get(c, Str(key2), val2, sizeof(val2));
+    if(ret == -1)
+      break;
+    if(c->childno == 0)
+      printf("%d: %s\n", iter, val2);
+    always_assert(strcmp(val1, val2) == 0);
+  }
+
+  checkasync(c, 2);
+  fprintf(stderr, "child %d checked %d\n", c->childno, iter);
+  printf("0\n");
+}
+
+// do a bunch of inserts to distinct keys.
+// rec2() checks that a prefix of those inserts are present.
+// meant to be interrupted by a crash/restart.
+void
+rec1(struct child *c)
+{
+  int i;
+  double t0 = now(), t1;
+
+  srandom(kvtest_first_seed + c->childno);
+
+  for(i = 0; !timeout[0]; i++){
+    char key[512], val[512];
+    long x = random();
+    sprintf(key, "%ld-%d-%d", x, i, c->childno);
+    sprintf(val, "%ld", x);
+    aput(c, Str(key), Str(val));
+  }
+  checkasync(c, 2);
+  t1 = now();
+
+  fprintf(stderr, "child %d: done %d %.0f put/s\n",
+          c->childno,
+          i,
+          i / (t1 - t0));
+  printf("%.0f\n", i / (t1 - t0));
+}
+
+void
+rec2(struct child *c)
+{
+  int i;
+
+  srandom(kvtest_first_seed + c->childno);
+
+  for(i = 0; ; i++){
+    char key[512], val[512], wanted[512];
+    long x = random();
+    sprintf(key, "%ld-%d-%d", x, i, c->childno);
+    sprintf(wanted, "%ld", x);
+    int ret = get(c, Str(key), val, sizeof(val));
+    if(ret == -1)
+      break;
+    val[ret] = 0;
+    if(strcmp(val, wanted) != 0){
+      fprintf(stderr, "oops key %s got %s wanted %s\n", key, val, wanted);
+      exit(1);
+    }
+  }
+
+  int i0 = i; // first missing record
+  for(i = i0+1; i < i0 + 10000; i++){
+    char key[512], val[512];
+    long x = random();
+    sprintf(key, "%ld-%d-%d", x, i, c->childno);
+    val[0] = 0;
+    int ret = get(c, Str(key), val, sizeof(val));
+    if(ret != -1){
+      printf("child %d: oops first missing %d but %d present\n",
+             c->childno, i0, i);
+      exit(1);
+    }
+  }
+  checkasync(c, 2);
+
+  fprintf(stderr, "correct prefix of %d records\n", i0);
+  printf("0\n");
+}
+
+// ask server to checkpoint
+void
+cpb(struct child *c)
+{
+    if (c->childno == 0)
+        c->conn->checkpoint(c->childno);
+    checkasync(c, 2);
+}
+
+// mimic the first benchmark from the VoltDB blog:
+//   https://voltdb.com/blog/key-value-benchmarking
+//   https://voltdb.com/blog/key-value-benchmark-faq
+//   http://community.voltdb.com/kvbenchdetails
+//   svn checkout http://svnmirror.voltdb.com/projects/kvbench/trunk
+// 500,000 items: 50-byte key, 12 KB value
+// volt1a creates the DB.
+// volt1b runs the benchmark.
+#define VOLT1N 500000
+#define VOLT1SIZE (12*1024)
+void
+volt1a(struct child *c)
+{
+  int i, j;
+  double t0 = now(), t1;
+  char *val = (char *) malloc(VOLT1SIZE + 1);
+  always_assert(val);
+
+  srandom(kvtest_first_seed + c->childno);
+
+  for(i = 0; i < VOLT1SIZE; i++)
+    val[i] = 'a' + (i % 26);
+  val[VOLT1SIZE] = '\0';
+
+  // XXX insert the keys in a random order to maintain
+  // tree balance.
+  int *keys = (int *) malloc(sizeof(int) * VOLT1N);
+  always_assert(keys);
+  for(i = 0; i < VOLT1N; i++)
+    keys[i] = i;
+  for(i = 0; i < VOLT1N; i++){
+    int x = random() % VOLT1N;
+    int tmp = keys[i];
+    keys[i] = keys[x];
+    keys[x] = tmp;
+  }
+
+  for(i = 0; i < VOLT1N; i++){
+    char key[100];
+    sprintf(key, "%-50d", keys[i]);
+    for(j = 0; j < 20; j++)
+      val[j] = 'a' + (j % 26);
+    sprintf(val, ">%d", keys[i]);
+    int j = strlen(val);
+    val[j] = '<';
+    always_assert(strlen(val) == VOLT1SIZE);
+    always_assert(strlen(key) == 50);
+    always_assert(isdigit(key[0]));
+    aput(c, Str(key), Str(val));
+  }
+  checkasync(c, 2);
+  t1 = now();
+
+  free(val);
+  free(keys);
+
+  Json result = Json().set("total", (long) (i / (t1 - t0)));
+  printf("%s\n", result.unparse().c_str());
+}
+
+// the actual volt1 benchmark.
+// get or update with equal probability.
+// their client pipelines many requests.
+// they use 8 client threads.
+// blog post says, for one server, VoltDB 17000, Cassandra 9740
+// this benchmark ends up being network or disk limited,
+// due to the huge values.
+void
+volt1b(struct child *c)
+{
+  int i, n, j;
+  double t0 = now(), t1;
+  char *wanted = (char *) malloc(VOLT1SIZE + 1);
+  always_assert(wanted);
+
+  for(i = 0; i < VOLT1SIZE; i++)
+    wanted[i] = 'a' + (i % 26);
+  wanted[VOLT1SIZE] = '\0';
+
+  srandom(kvtest_first_seed + c->childno);
+
+  for(i = 0; !timeout[0]; i++){
+    char key[100];
+    int x = random() % VOLT1N;
+    sprintf(key, "%-50d", x);
+    for(j = 0; j < 20; j++)
+      wanted[j] = 'a' + (j % 26);
+    sprintf(wanted, ">%d", x);
+    int j = strlen(wanted);
+    wanted[j] = '<';
+    if(i > 1)
+      checkasync(c, 1); // try to avoid deadlock, only 2 reqs outstanding
+    if((random() % 2) == 0)
+        aget(c, Str(key, 50), Str(wanted, VOLT1SIZE), 0);
+    else
+        aput(c, Str(key, 50), Str(wanted, VOLT1SIZE));
+  }
+  n = i;
+
+  checkasync(c, 2);
+  t1 = now();
+
+  Json result = Json().set("total", (long) (n / (t1 - t0)));
+  printf("%s\n", result.unparse().c_str());
+}
+
+// second VoltDB benchmark.
+// 500,000 pairs, 50-byte key, value is 50 32-bit ints.
+// pick a key, read one int, if odd, write a different int (same key).
+// i'm simulating columns by embedding column name in key: rowname-colname.
+// also the read/modify/write is not atomic.
+// volt2a creates the DB.
+// volt2b runs the benchmark.
+#define VOLT2N 500000
+#define VOLT2INTS 50
+void
+volt2a(struct child *c)
+{
+  int i, j, n = 0;
+  double t0 = now(), t1;
+
+  srandom(kvtest_first_seed + c->childno);
+
+  // XXX insert the keys in a random order to maintain
+  // tree balance.
+  int *keys = (int *) malloc(sizeof(int) * VOLT2N);
+  always_assert(keys);
+  for(i = 0; i < VOLT2N; i++)
+    keys[i] = i;
+  for(i = 0; i < VOLT2N; i++){
+    int x = random() % VOLT2N;
+    int tmp = keys[i];
+    keys[i] = keys[x];
+    keys[x] = tmp;
+  }
+
+  int subkeys[VOLT2INTS];
+  for(i = 0; i < VOLT2INTS; i++)
+    subkeys[i] = i;
+  for(i = 0; i < VOLT2INTS; i++){
+    int x = random() % VOLT2INTS;
+    int tmp = subkeys[i];
+    subkeys[i] = subkeys[x];
+    subkeys[x] = tmp;
+  }
+
+  for(i = 0; i < VOLT2N; i++){
+    for(j = 0; j < VOLT2INTS; j++){
+      char val[32], key[100];
+      int k;
+      sprintf(key, "%d-%d", keys[i], subkeys[j]);
+      for(k = strlen(key); k < 50; k++)
+        key[k] = ' ';
+      key[50] = '\0';
+      sprintf(val, "%ld", random());
+      aput(c, Str(key), Str(val));
+      n++;
+    }
+  }
+  checkasync(c, 2);
+  t1 = now();
+
+  free(keys);
+  Json result = Json().set("total", (long) (n / (t1 - t0)));
+  printf("%s\n", result.unparse().c_str());
+}
+
+// get callback
+void
+volt2b1(struct child *c, struct async *a, bool, const Str &val)
+{
+  int k = atoi(a->key);
+  int v = atoi(val.s);
+  if((v % 2) == 1){
+    char key[100], val[100];
+    sprintf(key, "%d-%ld", k, random() % VOLT2INTS);
+    for (int i = strlen(key); i < 50; i++)
+      key[i] = ' ';
+    sprintf(val, "%ld", random());
+    aput(c, Str(key, 50), Str(val));
+  }
+}
+
+void
+volt2b(struct child *c)
+{
+  int i, n;
+  double t0 = now(), t1;
+  srandom(kvtest_first_seed + c->childno);
+  for(i = 0; !timeout[0]; i++){
+    char key[100];
+    int x = random() % VOLT2N;
+    int y = random() % VOLT2INTS;
+    sprintf(key, "%d-%d", x, y);
+    int j;
+    for(j = strlen(key); j < 50; j++)
+      key[j] = ' ';
+    aget(c, Str(key, 50), Str(), volt2b1);
+  }
+  n = i;
+
+  checkasync(c, 2);
+  t1 = now();
+
+  Json result = Json().set("total", (long) (n / (t1 - t0)));
+  printf("%s\n", result.unparse().c_str());
+}
+
+using std::vector;
+using std::string;
+
+void
+scantest(struct child *c)
+{
+  int i;
+
+  srandom(kvtest_first_seed + c->childno);
+
+  for(i = 100; i < 200; i++){
+    char key[32], val[32];
+    int kl = sprintf(key, "k%04d", i);
+    sprintf(val, "v%04d", i);
+    aput(c, Str(key, kl), Str(val));
+  }
+
+  checkasync(c, 2);
+
+  for(i = 90; i < 210; i++){
+    char key[32];
+    sprintf(key, "k%04d", i);
+    int wanted = random() % 10;
+    c->conn->sendscanwhole(key, wanted, 1);
+    c->conn->flush();
+
+    {
+        const Json& result = c->conn->receive();
+        always_assert(result && result[0] == 1);
+        int n = (result.size() - 2) / 2;
+        if(i <= 200 - wanted){
+            always_assert(n == wanted);
+        } else if(i <= 200){
+            always_assert(n == 200 - i);
+        } else {
+            always_assert(n == 0);
+        }
+        int k0 = (i < 100 ? 100 : i);
+        int j, ki, off = 2;
+        for(j = k0, ki = 0; j < k0 + wanted && j < 200; j++, ki++, off += 2){
+            char xkey[32], xval[32];
+            sprintf(xkey, "k%04d", j);
+            sprintf(xval, "v%04d", j);
+            if (!result[off].as_s().equals(xkey)) {
+                fprintf(stderr, "Assertion failed @%d: strcmp(%s, %s) == 0\n", ki, result[off].as_s().c_str(), xkey);
+                always_assert(0);
+            }
+            always_assert(result[off + 1].as_s().equals(xval));
+        }
+    }
+
+    {
+        sprintf(key, "k%04d-a", i);
+        c->conn->sendscanwhole(key, 1, 1);
+        c->conn->flush();
+
+        const Json& result = c->conn->receive();
+        always_assert(result && result[0] == 1);
+        int n = (result.size() - 2) / 2;
+        if(i >= 100 && i < 199){
+            always_assert(n == 1);
+            sprintf(key, "k%04d", i+1);
+            always_assert(result[2].as_s().equals(key));
+        }
+    }
+  }
+
+  c->conn->sendscanwhole("k015", 10, 1);
+  c->conn->flush();
+
+  const Json& result = c->conn->receive();
+  always_assert(result && result[0] == 1);
+  int n = (result.size() - 2) / 2;
+  always_assert(n == 10);
+  always_assert(result[2].as_s().equals("k0150"));
+  always_assert(result[3].as_s().equals("v0150"));
+
+  fprintf(stderr, "scantest OK\n");
+  printf("0\n");
+}
diff --git a/silo/masstree/mtclient.hh b/silo/masstree/mtclient.hh
new file mode 100644 (file)
index 0000000..3413166
--- /dev/null
@@ -0,0 +1,219 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef KVC_HH
+#define KVC_HH 1
+#include "kvproto.hh"
+#include "kvrow.hh"
+#include "json.hh"
+#include "msgpack.hh"
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <string>
+#include <queue>
+#include <vector>
+
+class KVConn {
+  public:
+    KVConn(const char *server, int port, int target_core = -1)
+        : inbuf_(new char[inbufsz]), inbufpos_(0), inbuflen_(0),
+          j_(Json::make_array()) {
+        struct hostent *ent = gethostbyname(server);
+        always_assert(ent);
+        int fd = socket(AF_INET, SOCK_STREAM, 0);
+        always_assert(fd > 0);
+        fdtoclose_ = fd;
+        int yes = 1;
+        always_assert(fd >= 0);
+        setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
+
+        struct sockaddr_in sin;
+        memset(&sin, 0, sizeof(sin));
+        sin.sin_family = AF_INET;
+        sin.sin_port = htons(port);
+        memcpy(&sin.sin_addr.s_addr, ent->h_addr, ent->h_length);
+        int r = connect(fd, (const struct sockaddr *)&sin, sizeof(sin));
+        if (r) {
+            perror("connect");
+            exit(EXIT_FAILURE);
+        }
+
+        infd_ = fd;
+        out_ = new_kvout(fd, 64*1024);
+        handshake(target_core);
+    }
+    KVConn(int fd, bool tcp)
+        : inbuf_(new char[inbufsz]), inbufpos_(0), inbuflen_(0), infd_(fd),
+          j_(Json::make_array()) {
+        out_ = new_kvout(fd, 64*1024);
+        fdtoclose_ = -1;
+        if (tcp)
+            handshake(-1);
+    }
+    ~KVConn() {
+        if (fdtoclose_ >= 0)
+            close(fdtoclose_);
+        free_kvout(out_);
+        delete[] inbuf_;
+        for (auto x : oldinbuf_)
+            delete[] x;
+    }
+    void sendgetwhole(Str key, unsigned seq) {
+        j_.resize(3);
+        j_[0] = seq;
+        j_[1] = Cmd_Get;
+        j_[2] = String::make_stable(key);
+        send();
+    }
+    void sendgetcol(Str key, int col, unsigned seq) {
+        j_.resize(4);
+        j_[0] = seq;
+        j_[1] = Cmd_Get;
+        j_[2] = String::make_stable(key);
+        j_[3] = col;
+        send();
+    }
+    void sendget(Str key, const std::vector<unsigned>& f, unsigned seq) {
+        j_.resize(4);
+        j_[0] = seq;
+        j_[1] = Cmd_Get;
+        j_[2] = String::make_stable(key);
+        j_[3] = Json(f.begin(), f.end());
+        send();
+    }
+
+    void sendputcol(Str key, int col, Str val, unsigned seq) {
+        j_.resize(5);
+        j_[0] = seq;
+        j_[1] = Cmd_Put;
+        j_[2] = String::make_stable(key);
+        j_[3] = col;
+        j_[4] = String::make_stable(val);
+        send();
+    }
+    void sendputwhole(Str key, Str val, unsigned seq) {
+        j_.resize(3);
+        j_[0] = seq;
+        j_[1] = Cmd_Replace;
+        j_[2] = String::make_stable(key);
+        j_[3] = String::make_stable(val);
+        send();
+    }
+    void sendremove(Str key, unsigned seq) {
+        j_.resize(3);
+        j_[0] = seq;
+        j_[1] = Cmd_Remove;
+        j_[2] = String::make_stable(key);
+        send();
+    }
+
+    void sendscanwhole(Str firstkey, int numpairs, unsigned seq) {
+        j_.resize(4);
+        j_[0] = seq;
+        j_[1] = Cmd_Scan;
+        j_[2] = String::make_stable(firstkey);
+        j_[3] = numpairs;
+        send();
+    }
+    void sendscan(Str firstkey, const std::vector<unsigned>& f,
+                  int numpairs, unsigned seq) {
+        j_.resize(5);
+        j_[0] = seq;
+        j_[1] = Cmd_Scan;
+        j_[2] = String::make_stable(firstkey);
+        j_[3] = numpairs;
+        j_[4] = Json(f.begin(), f.end());
+        send();
+    }
+
+    void checkpoint(int childno) {
+       always_assert(childno == 0);
+        fprintf(stderr, "asking for a checkpoint\n");
+        j_.resize(2);
+        j_[0] = 0;
+        j_[1] = Cmd_Checkpoint;
+        send();
+        flush();
+
+        printf("sent\n");
+        (void) receive();
+    }
+
+    void flush() {
+        kvflush(out_);
+    }
+
+    int check(int tryhard) {
+        if (inbufpos_ == inbuflen_ && tryhard)
+            hard_check(tryhard);
+        return inbuflen_ - inbufpos_;
+    }
+
+    const Json& receive() {
+        while (!parser_.done() && check(2))
+            inbufpos_ += parser_.consume(inbuf_ + inbufpos_,
+                                         inbuflen_ - inbufpos_,
+                                         String::make_stable(inbuf_, inbufsz));
+        if (parser_.success() && parser_.result().is_a())
+            parser_.reset();
+        else
+            parser_.result() = Json();
+        return parser_.result();
+    }
+
+  private:
+    enum { inbufsz = 64 * 1024, inbufrefill = 56 * 1024 };
+    char* inbuf_;
+    int inbufpos_;
+    int inbuflen_;
+    std::vector<char*> oldinbuf_;
+    int infd_;
+
+    struct kvout *out_;
+
+    Json j_;
+    msgpack::streaming_parser parser_;
+
+    int fdtoclose_;
+    int partition_;
+
+    void handshake(int target_core) {
+        j_.resize(3);
+        j_[0] = 0;
+        j_[1] = Cmd_Handshake;
+        j_[2] = Json::make_object().set("core", target_core)
+            .set("maxkeylen", MASSTREE_MAXKEYLEN);
+        send();
+        kvflush(out_);
+
+        const Json& result = receive();
+        if (!result.is_a()
+            || result[1] != Cmd_Handshake + 1
+            || !result[2]) {
+            fprintf(stderr, "Incompatible kvdb protocol\n");
+            exit(EXIT_FAILURE);
+        }
+        partition_ = result[3].as_i();
+    }
+    inline void send() {
+        msgpack::unparse(*out_, j_);
+    }
+    void hard_check(int tryhard);
+};
+
+#endif
diff --git a/silo/masstree/mtcounters.hh b/silo/masstree/mtcounters.hh
new file mode 100644 (file)
index 0000000..e68735e
--- /dev/null
@@ -0,0 +1,55 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MTCOUNTERS_HH
+#define MTCOUNTERS_HH 1
+
+enum memtag {
+    memtag_none = 0x0,
+    memtag_value = 0x1,
+    memtag_limbo = 0x5,
+    memtag_masstree_leaf = 0x10,
+    memtag_masstree_internode = 0x11,
+    memtag_masstree_ksuffixes = 0x12,
+    memtag_masstree_gc = 0x13
+};
+
+enum threadcounter {
+    // order is important among tc_alloc constants:
+    tc_alloc,
+    tc_alloc_value = tc_alloc,
+    tc_alloc_other = tc_alloc + 1,
+    // end tc_alloc constants
+    tc_gc,
+    tc_limbo_slots,
+    tc_replay_create_delta,
+    tc_replay_remove_delta,
+    tc_root_retry,
+    tc_internode_retry,
+    tc_leaf_retry,
+    tc_leaf_walk,
+    // order is important among tc_stable constants:
+    tc_stable,
+    tc_stable_internode_insert = tc_stable + 0,
+    tc_stable_internode_split = tc_stable + 1,
+    tc_stable_leaf_insert = tc_stable + 2,
+    tc_stable_leaf_split = tc_stable + 3,
+    // end tc_stable constants
+    tc_internode_lock,
+    tc_leaf_lock,
+    tc_max
+};
+
+#endif
diff --git a/silo/masstree/mtd.cc b/silo/masstree/mtd.cc
new file mode 100644 (file)
index 0000000..e66f70a
--- /dev/null
@@ -0,0 +1,1704 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+// -*- mode: c++ -*-
+// kvd: key/value server
+//
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/select.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <limits.h>
+#if HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#endif
+#if __linux__
+#include <asm-generic/mman.h>
+#endif
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+#include <signal.h>
+#include <errno.h>
+#ifdef __linux__
+#include <malloc.h>
+#endif
+#include "nodeversion.hh"
+#include "kvstats.hh"
+#include "json.hh"
+#include "kvtest.hh"
+#include "kvrandom.hh"
+#include "clp.h"
+#include "log.hh"
+#include "checkpoint.hh"
+#include "file.hh"
+#include "kvproto.hh"
+#include "query_masstree.hh"
+#include "masstree_tcursor.hh"
+#include "masstree_insert.hh"
+#include "masstree_remove.hh"
+#include "masstree_scan.hh"
+#include "msgpack.hh"
+#include <algorithm>
+#include <deque>
+using lcdf::StringAccum;
+
+enum { CKState_Quit, CKState_Uninit, CKState_Ready, CKState_Go };
+
+volatile bool timeout[2] = {false, false};
+double duration[2] = {10, 0};
+
+Masstree::default_table *tree;
+
+// all default to the number of cores
+static int udpthreads = 0;
+static int tcpthreads = 0;
+static int nckthreads = 0;
+static int testthreads = 0;
+static int nlogger = 0;
+static std::vector<int> cores;
+
+static bool logging = true;
+static bool pinthreads = false;
+static bool recovery_only = false;
+volatile uint64_t globalepoch = 1;     // global epoch, updated by main thread regularly
+static int port = 2117;
+static uint64_t test_limit = ~uint64_t(0);
+static int doprint = 0;
+int kvtest_first_seed = 31949;
+
+static volatile sig_atomic_t go_quit = 0;
+static int quit_pipe[2];
+
+static std::vector<const char*> logdirs;
+static std::vector<const char*> ckpdirs;
+
+static logset* logs;
+volatile bool recovering = false; // so don't add log entries, and free old value immediately
+
+static double checkpoint_interval = 1000000;
+static kvepoch_t ckp_gen = 0; // recover from checkpoint
+static ckstate *cks = NULL; // checkpoint status of all checkpointing threads
+static pthread_cond_t rec_cond;
+pthread_mutex_t rec_mu;
+static int rec_nactive;
+static int rec_state = REC_NONE;
+
+kvtimestamp_t initial_timestamp;
+
+static pthread_cond_t checkpoint_cond;
+static pthread_mutex_t checkpoint_mu;
+
+static void prepare_thread(threadinfo *ti);
+static int* tcp_thread_pipes;
+static void* tcp_threadfunc(threadinfo* ti);
+static void* udp_threadfunc(threadinfo* ti);
+
+static void log_init();
+static void recover(threadinfo *);
+static kvepoch_t read_checkpoint(threadinfo*, const char *path);
+
+static void* conc_checkpointer(threadinfo* ti);
+static void recovercheckpoint(threadinfo *ti);
+
+static void *canceling(void *);
+static void catchint(int);
+static void epochinc(int);
+
+/* running local tests */
+void test_timeout(int) {
+    size_t n;
+    for (n = 0; n < arraysize(timeout) && timeout[n]; ++n)
+        /* do nothing */;
+    if (n < arraysize(timeout)) {
+        timeout[n] = true;
+        if (n + 1 < arraysize(timeout) && duration[n + 1])
+            xalarm(duration[n + 1]);
+    }
+}
+
+struct kvtest_client {
+    kvtest_client()
+        : checks_(0), kvo_() {
+    }
+    kvtest_client(const char *testname)
+        : testname_(testname), checks_(0), kvo_() {
+    }
+
+    int id() const {
+        return ti_->index();
+    }
+    int nthreads() const {
+        return testthreads;
+    }
+    void set_thread(threadinfo *ti) {
+        ti_ = ti;
+    }
+    void register_timeouts(int n) {
+        always_assert(n <= (int) arraysize(::timeout));
+        for (int i = 1; i < n; ++i)
+            if (duration[i] == 0)
+                duration[i] = 0;//duration[i - 1];
+    }
+    bool timeout(int which) const {
+        return ::timeout[which];
+    }
+    uint64_t limit() const {
+        return test_limit;
+    }
+    Json param(const String&) const {
+        return Json();
+    }
+    double now() const {
+        return ::now();
+    }
+
+    void get(long ikey, Str *value);
+    void get(const Str &key);
+    void get(long ikey) {
+        quick_istr key(ikey);
+        get(key.string());
+    }
+    void get_check(const Str &key, const Str &expected);
+    void get_check(const char *key, const char *expected) {
+        get_check(Str(key, strlen(key)), Str(expected, strlen(expected)));
+    }
+    void get_check(long ikey, long iexpected) {
+        quick_istr key(ikey), expected(iexpected);
+        get_check(key.string(), expected.string());
+    }
+    void get_check_key8(long ikey, long iexpected) {
+        quick_istr key(ikey, 8), expected(iexpected);
+        get_check(key.string(), expected.string());
+    }
+    void get_check_key10(long ikey, long iexpected) {
+        quick_istr key(ikey, 10), expected(iexpected);
+        get_check(key.string(), expected.string());
+    }
+    void get_col_check(const Str &key, int col, const Str &expected);
+    void get_col_check_key10(long ikey, int col, long iexpected) {
+        quick_istr key(ikey, 10), expected(iexpected);
+        get_col_check(key.string(), col, expected.string());
+    }
+    bool get_sync(long ikey);
+
+    void put(const Str &key, const Str &value);
+    void put(const char *key, const char *val) {
+        put(Str(key, strlen(key)), Str(val, strlen(val)));
+    }
+    void put(long ikey, long ivalue) {
+        quick_istr key(ikey), value(ivalue);
+        put(key.string(), value.string());
+    }
+    void put_key8(long ikey, long ivalue) {
+        quick_istr key(ikey, 8), value(ivalue);
+        put(key.string(), value.string());
+    }
+    void put_key10(long ikey, long ivalue) {
+        quick_istr key(ikey, 10), value(ivalue);
+        put(key.string(), value.string());
+    }
+    void put_col(const Str &key, int col, const Str &value);
+    void put_col_key10(long ikey, int col, long ivalue) {
+        quick_istr key(ikey, 10), value(ivalue);
+        put_col(key.string(), col, value.string());
+    }
+
+    bool remove_sync(long ikey);
+
+    void puts_done() {
+    }
+    void wait_all() {
+    }
+    void rcu_quiesce() {
+    }
+    String make_message(StringAccum &sa) const;
+    void notice(const char *fmt, ...);
+    void fail(const char *fmt, ...);
+    const Json& report(const Json& x) {
+        return report_.merge(x);
+    }
+    void finish() {
+        fprintf(stderr, "%d: %s\n", ti_->index(), report_.unparse().c_str());
+    }
+    threadinfo *ti_;
+    query<row_type> q_[10];
+    const char *testname_;
+    kvrandom_lcg_nr rand;
+    int checks_;
+    Json report_;
+    struct kvout *kvo_;
+    static volatile int failing;
+};
+
+volatile int kvtest_client::failing;
+
+void kvtest_client::get(long ikey, Str *value)
+{
+    quick_istr key(ikey);
+    if (!q_[0].run_get1(tree->table(), key.string(), 0, *value, *ti_))
+        *value = Str();
+}
+
+void kvtest_client::get(const Str &key)
+{
+    Str val;
+    (void) q_[0].run_get1(tree->table(), key, 0, val, *ti_);
+}
+
+void kvtest_client::get_check(const Str &key, const Str &expected)
+{
+    Str val;
+    if (!q_[0].run_get1(tree->table(), key, 0, val, *ti_)) {
+        fail("get(%.*s) failed (expected %.*s)\n", key.len, key.s, expected.len, expected.s);
+        return;
+    }
+    if (val.len != expected.len || memcmp(val.s, expected.s, val.len) != 0)
+        fail("get(%.*s) returned unexpected value %.*s (expected %.*s)\n", key.len, key.s,
+             std::min(val.len, 40), val.s, std::min(expected.len, 40), expected.s);
+    else
+        ++checks_;
+}
+
+void kvtest_client::get_col_check(const Str &key, int col, const Str &expected)
+{
+    Str val;
+    if (!q_[0].run_get1(tree->table(), key, col, val, *ti_)) {
+        fail("get.%d(%.*s) failed (expected %.*s)\n", col, key.len, key.s,
+             expected.len, expected.s);
+        return;
+    }
+    if (val.len != expected.len || memcmp(val.s, expected.s, val.len) != 0)
+        fail("get.%d(%.*s) returned unexpected value %.*s (expected %.*s)\n",
+             col, key.len, key.s, std::min(val.len, 40), val.s,
+             std::min(expected.len, 40), expected.s);
+    else
+        ++checks_;
+}
+
+bool kvtest_client::get_sync(long ikey) {
+    quick_istr key(ikey);
+    Str val;
+    return q_[0].run_get1(tree->table(), key.string(), 0, val, *ti_);
+}
+
+void kvtest_client::put(const Str &key, const Str &value) {
+    while (failing)
+        /* do nothing */;
+    q_[0].run_replace(tree->table(), key, value, *ti_);
+    if (ti_->logger()) // NB may block
+        ti_->logger()->record(logcmd_replace, q_[0].query_times(), key, value);
+}
+
+void kvtest_client::put_col(const Str &key, int col, const Str &value) {
+    while (failing)
+        /* do nothing */;
+#if !MASSTREE_ROW_TYPE_STR
+    if (!kvo_)
+        kvo_ = new_kvout(-1, 2048);
+    Json req[2] = {Json(col), Json(String::make_stable(value))};
+    (void) q_[0].run_put(tree->table(), key, &req[0], &req[2], *ti_);
+    if (ti_->logger()) // NB may block
+        ti_->logger()->record(logcmd_put, q_[0].query_times(), key,
+                              &req[0], &req[2]);
+#else
+    (void) key, (void) col, (void) value;
+    assert(0);
+#endif
+}
+
+bool kvtest_client::remove_sync(long ikey) {
+    quick_istr key(ikey);
+    bool removed = q_[0].run_remove(tree->table(), key.string(), *ti_);
+    if (removed && ti_->logger()) // NB may block
+        ti_->logger()->record(logcmd_remove, q_[0].query_times(), key.string(), Str());
+    return removed;
+}
+
+String kvtest_client::make_message(StringAccum &sa) const {
+    const char *begin = sa.begin();
+    while (begin != sa.end() && isspace((unsigned char) *begin))
+        ++begin;
+    String s = String(begin, sa.end());
+    if (!s.empty() && s.back() != '\n')
+        s += '\n';
+    return s;
+}
+
+void kvtest_client::notice(const char *fmt, ...) {
+    va_list val;
+    va_start(val, fmt);
+    String m = make_message(StringAccum().vsnprintf(500, fmt, val));
+    va_end(val);
+    if (m)
+        fprintf(stderr, "%d: %s", ti_->index(), m.c_str());
+}
+
+void kvtest_client::fail(const char *fmt, ...) {
+    static nodeversion failing_lock(false);
+    static nodeversion fail_message_lock(false);
+    static String fail_message;
+    failing = 1;
+
+    va_list val;
+    va_start(val, fmt);
+    String m = make_message(StringAccum().vsnprintf(500, fmt, val));
+    va_end(val);
+    if (!m)
+        m = "unknown failure";
+
+    fail_message_lock.lock();
+    if (fail_message != m) {
+        fail_message = m;
+        fprintf(stderr, "%d: %s", ti_->index(), m.c_str());
+    }
+    fail_message_lock.unlock();
+
+    if (doprint) {
+        failing_lock.lock();
+        fprintf(stdout, "%d: %s", ti_->index(), m.c_str());
+        tree->print(stdout, 0);
+        fflush(stdout);
+    }
+
+    always_assert(0);
+}
+
+static void* testgo(threadinfo* ti) {
+    kvtest_client *kc = (kvtest_client*) ti->thread_data();
+    prepare_thread(kc->ti_);
+
+    if (strcmp(kc->testname_, "rw1") == 0)
+        kvtest_rw1(*kc);
+    else if (strcmp(kc->testname_, "rw2") == 0)
+        kvtest_rw2(*kc);
+    else if (strcmp(kc->testname_, "rw3") == 0)
+        kvtest_rw3(*kc);
+    else if (strcmp(kc->testname_, "rw4") == 0)
+        kvtest_rw4(*kc);
+    else if (strcmp(kc->testname_, "rwsmall24") == 0)
+        kvtest_rwsmall24(*kc);
+    else if (strcmp(kc->testname_, "rwsep24") == 0)
+        kvtest_rwsep24(*kc);
+    else if (strcmp(kc->testname_, "palma") == 0)
+        kvtest_palma(*kc);
+    else if (strcmp(kc->testname_, "palmb") == 0)
+        kvtest_palmb(*kc);
+    else if (strcmp(kc->testname_, "rw16") == 0)
+        kvtest_rw16(*kc);
+    else if (strcmp(kc->testname_, "rw5") == 0
+             || strcmp(kc->testname_, "rw1fixed") == 0)
+        kvtest_rw1fixed(*kc);
+    else if (strcmp(kc->testname_, "ycsbk") == 0)
+        kvtest_ycsbk(*kc);
+    else if (strcmp(kc->testname_, "wd1") == 0)
+        kvtest_wd1(10000000, 1, *kc);
+    else if (strcmp(kc->testname_, "wd1check") == 0)
+        kvtest_wd1_check(10000000, 1, *kc);
+    else if (strcmp(kc->testname_, "w1") == 0)
+        kvtest_w1_seed(*kc, kvtest_first_seed + kc->id());
+    else if (strcmp(kc->testname_, "r1") == 0)
+        kvtest_r1_seed(*kc, kvtest_first_seed + kc->id());
+    else if (strcmp(kc->testname_, "wcol1") == 0)
+        kvtest_wcol1at(*kc, kc->id() % 24, kvtest_first_seed + kc->id() % 48, 5000000);
+    else if (strcmp(kc->testname_, "rcol1") == 0)
+        kvtest_rcol1at(*kc, kc->id() % 24, kvtest_first_seed + kc->id() % 48, 5000000);
+    else
+        kc->fail("unknown test '%s'", kc->testname_);
+    return 0;
+}
+
+static const char * const kvstats_name[] = {
+    "ops", "ops_per_sec", "puts", "gets", "scans", "puts_per_sec", "gets_per_sec", "scans_per_sec"
+};
+
+void runtest(const char *testname, int nthreads) {
+    std::vector<kvtest_client> clients(nthreads, kvtest_client(testname));
+    ::testthreads = nthreads;
+    for (int i = 0; i < nthreads; ++i)
+        clients[i].set_thread(threadinfo::make(threadinfo::TI_PROCESS, i));
+    bzero((void *)timeout, sizeof(timeout));
+    signal(SIGALRM, test_timeout);
+    if (duration[0])
+        xalarm(duration[0]);
+    for (int i = 0; i < nthreads; ++i) {
+        int r = clients[i].ti_->run(testgo, &clients[i]);
+        always_assert(r == 0);
+    }
+    for (int i = 0; i < nthreads; ++i)
+        pthread_join(clients[i].ti_->threadid(), 0);
+
+    kvstats kvs[arraysize(kvstats_name)];
+    for (int i = 0; i < nthreads; ++i)
+        for (int j = 0; j < (int) arraysize(kvstats_name); ++j)
+            if (double x = clients[i].report_.get_d(kvstats_name[j]))
+                kvs[j].add(x);
+    for (int j = 0; j < (int) arraysize(kvstats_name); ++j)
+        kvs[j].print_report(kvstats_name[j]);
+}
+
+
+struct conn {
+    int fd;
+    enum { inbufsz = 20 * 1024, inbufrefill = 16 * 1024 };
+
+    conn(int s)
+        : fd(s), inbuf_(new char[inbufsz]),
+          inbufpos_(0), inbuflen_(0), kvout(new_kvout(s, 20 * 1024)),
+          inbuftotal_(0) {
+    }
+    ~conn() {
+        close(fd);
+        free_kvout(kvout);
+        delete[] inbuf_;
+        for (char* x : oldinbuf_)
+            delete[] x;
+    }
+
+    Json& receive() {
+        while (!parser_.done() && check(2))
+            inbufpos_ += parser_.consume(inbuf_ + inbufpos_,
+                                         inbuflen_ - inbufpos_,
+                                         String::make_stable(inbuf_, inbufsz));
+        if (parser_.success() && parser_.result().is_a())
+            parser_.reset();
+        else
+            parser_.result() = Json();
+        return parser_.result();
+    }
+
+    int check(int tryhard) {
+        if (inbufpos_ == inbuflen_ && tryhard)
+            hard_check(tryhard);
+        return inbuflen_ - inbufpos_;
+    }
+
+    uint64_t xposition() const {
+        return inbuftotal_ + inbufpos_;
+    }
+    Str recent_string(uint64_t xposition) const {
+        if (xposition - inbuftotal_ <= unsigned(inbufpos_))
+            return Str(inbuf_ + (xposition - inbuftotal_),
+                       inbuf_ + inbufpos_);
+        else
+            return Str();
+    }
+
+  private:
+    char* inbuf_;
+    int inbufpos_;
+    int inbuflen_;
+    std::vector<char*> oldinbuf_;
+    msgpack::streaming_parser parser_;
+  public:
+    struct kvout *kvout;
+  private:
+    uint64_t inbuftotal_;
+
+    void hard_check(int tryhard);
+};
+
+void conn::hard_check(int tryhard) {
+    masstree_precondition(inbufpos_ == inbuflen_);
+    if (parser_.empty()) {
+        inbuftotal_ += inbufpos_;
+        inbufpos_ = inbuflen_ = 0;
+        for (auto x : oldinbuf_)
+            delete[] x;
+        oldinbuf_.clear();
+    } else if (inbufpos_ == inbufsz) {
+        oldinbuf_.push_back(inbuf_);
+        inbuf_ = new char[inbufsz];
+        inbuftotal_ += inbufpos_;
+        inbufpos_ = inbuflen_ = 0;
+    }
+    if (tryhard == 1) {
+        fd_set rfds;
+        FD_ZERO(&rfds);
+        FD_SET(fd, &rfds);
+        struct timeval tv = {0, 0};
+        if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0)
+            return;
+    } else
+        kvflush(kvout);
+
+    ssize_t r = read(fd, inbuf_ + inbufpos_, inbufsz - inbufpos_);
+    if (r != -1)
+        inbuflen_ += r;
+}
+
+struct conninfo {
+    int s;
+    Json handshake;
+};
+
+
+/* main loop */
+
+enum { clp_val_suffixdouble = Clp_ValFirstUser };
+enum { opt_nolog = 1, opt_pin, opt_logdir, opt_port, opt_ckpdir, opt_duration,
+       opt_test, opt_test_name, opt_threads, opt_cores,
+       opt_print, opt_norun, opt_checkpoint, opt_limit };
+static const Clp_Option options[] = {
+    { "no-log", 0, opt_nolog, 0, 0 },
+    { 0, 'n', opt_nolog, 0, 0 },
+    { "no-run", 0, opt_norun, 0, 0 },
+    { "pin", 'p', opt_pin, 0, Clp_Negate },
+    { "logdir", 0, opt_logdir, Clp_ValString, 0 },
+    { "ld", 0, opt_logdir, Clp_ValString, 0 },
+    { "checkpoint", 'c', opt_checkpoint, Clp_ValDouble, Clp_Optional | Clp_Negate },
+    { "ckp", 0, opt_checkpoint, Clp_ValDouble, Clp_Optional | Clp_Negate },
+    { "ckpdir", 0, opt_ckpdir, Clp_ValString, 0 },
+    { "ckdir", 0, opt_ckpdir, Clp_ValString, 0 },
+    { "cd", 0, opt_ckpdir, Clp_ValString, 0 },
+    { "port", 0, opt_port, Clp_ValInt, 0 },
+    { "duration", 'd', opt_duration, Clp_ValDouble, 0 },
+    { "limit", 'l', opt_limit, clp_val_suffixdouble, 0 },
+    { "test", 0, opt_test, Clp_ValString, 0 },
+    { "test-rw1", 0, opt_test_name, 0, 0 },
+    { "test-rw2", 0, opt_test_name, 0, 0 },
+    { "test-rw3", 0, opt_test_name, 0, 0 },
+    { "test-rw4", 0, opt_test_name, 0, 0 },
+    { "test-rw5", 0, opt_test_name, 0, 0 },
+    { "test-rw16", 0, opt_test_name, 0, 0 },
+    { "test-palm", 0, opt_test_name, 0, 0 },
+    { "test-ycsbk", 0, opt_test_name, 0, 0 },
+    { "test-rw1fixed", 0, opt_test_name, 0, 0 },
+    { "threads", 'j', opt_threads, Clp_ValInt, 0 },
+    { "cores", 0, opt_cores, Clp_ValString, 0 },
+    { "print", 0, opt_print, 0, Clp_Negate }
+};
+
+int
+main(int argc, char *argv[])
+{
+  using std::swap;
+  int s, ret, yes = 1, i = 1, firstcore = -1, corestride = 1;
+  const char *dotest = 0;
+  nlogger = tcpthreads = udpthreads = nckthreads = sysconf(_SC_NPROCESSORS_ONLN);
+  Clp_Parser *clp = Clp_NewParser(argc, argv, (int) arraysize(options), options);
+  Clp_AddType(clp, clp_val_suffixdouble, Clp_DisallowOptions, clp_parse_suffixdouble, 0);
+  int opt;
+  while ((opt = Clp_Next(clp)) >= 0) {
+      switch (opt) {
+      case opt_nolog:
+          logging = false;
+          break;
+      case opt_pin:
+          pinthreads = !clp->negated;
+          break;
+      case opt_threads:
+          nlogger = tcpthreads = udpthreads = nckthreads = clp->val.i;
+          break;
+      case opt_logdir: {
+          const char *s = strtok((char *) clp->vstr, ",");
+          for (; s; s = strtok(NULL, ","))
+              logdirs.push_back(s);
+          break;
+      }
+      case opt_ckpdir: {
+          const char *s = strtok((char *) clp->vstr, ",");
+          for (; s; s = strtok(NULL, ","))
+              ckpdirs.push_back(s);
+          break;
+      }
+      case opt_checkpoint:
+          if (clp->negated || (clp->have_val && clp->val.d <= 0))
+              checkpoint_interval = -1;
+          else if (clp->have_val)
+              checkpoint_interval = clp->val.d;
+          else
+              checkpoint_interval = 30;
+          break;
+      case opt_port:
+          port = clp->val.i;
+          break;
+      case opt_duration:
+          duration[0] = clp->val.d;
+          break;
+      case opt_limit:
+          test_limit = (uint64_t) clp->val.d;
+          break;
+      case opt_test:
+          dotest = clp->vstr;
+          break;
+      case opt_test_name:
+          dotest = clp->option->long_name + 5;
+          break;
+      case opt_print:
+          doprint = !clp->negated;
+          break;
+      case opt_cores:
+          if (firstcore >= 0 || cores.size() > 0) {
+              Clp_OptionError(clp, "%<%O%> already given");
+              exit(EXIT_FAILURE);
+          } else {
+              const char *plus = strchr(clp->vstr, '+');
+              Json ij = Json::parse(clp->vstr),
+                  aj = Json::parse(String("[") + String(clp->vstr) + String("]")),
+                  pj1 = Json::parse(plus ? String(clp->vstr, plus) : "x"),
+                  pj2 = Json::parse(plus ? String(plus + 1) : "x");
+              for (int i = 0; aj && i < aj.size(); ++i)
+                  if (!aj[i].is_int() || aj[i].to_i() < 0)
+                      aj = Json();
+              if (ij && ij.is_int() && ij.to_i() >= 0)
+                  firstcore = ij.to_i(), corestride = 1;
+              else if (pj1 && pj2 && pj1.is_int() && pj1.to_i() >= 0 && pj2.is_int())
+                  firstcore = pj1.to_i(), corestride = pj2.to_i();
+              else if (aj) {
+                  for (int i = 0; i < aj.size(); ++i)
+                      cores.push_back(aj[i].to_i());
+              } else {
+                  Clp_OptionError(clp, "bad %<%O%>, expected %<CORE1%>, %<CORE1+STRIDE%>, or %<CORE1,CORE2,...%>");
+                  exit(EXIT_FAILURE);
+              }
+          }
+          break;
+      case opt_norun:
+          recovery_only = true;
+          break;
+      default:
+          fprintf(stderr, "Usage: kvd [-np] [--ld dir1[,dir2,...]] [--cd dir1[,dir2,...]]\n");
+          exit(EXIT_FAILURE);
+      }
+  }
+  Clp_DeleteParser(clp);
+  if (logdirs.empty())
+      logdirs.push_back(".");
+  if (ckpdirs.empty())
+      ckpdirs.push_back(".");
+  if (firstcore < 0)
+      firstcore = cores.size() ? cores.back() + 1 : 0;
+  for (; (int) cores.size() < udpthreads; firstcore += corestride)
+      cores.push_back(firstcore);
+
+  // for -pg profiling
+  signal(SIGINT, catchint);
+
+  // log epoch starts at 1
+  global_log_epoch = 1;
+  global_wake_epoch = 0;
+  log_epoch_interval.tv_sec = 0;
+  log_epoch_interval.tv_usec = 200000;
+
+  // increment the global epoch every second
+  if (!dotest) {
+      signal(SIGALRM, epochinc);
+      struct itimerval etimer;
+      etimer.it_interval.tv_sec = 1;
+      etimer.it_interval.tv_usec = 0;
+      etimer.it_value.tv_sec = 1;
+      etimer.it_value.tv_usec = 0;
+      ret = setitimer(ITIMER_REAL, &etimer, NULL);
+      always_assert(ret == 0);
+  }
+
+  // arrange for a per-thread threadinfo pointer
+  ret = pthread_key_create(&threadinfo::key, 0);
+  always_assert(ret == 0);
+
+  // for parallel recovery
+  ret = pthread_cond_init(&rec_cond, 0);
+  always_assert(ret == 0);
+  ret = pthread_mutex_init(&rec_mu, 0);
+  always_assert(ret == 0);
+
+  // for waking up the checkpoint thread
+  ret = pthread_cond_init(&checkpoint_cond, 0);
+  always_assert(ret == 0);
+  ret = pthread_mutex_init(&checkpoint_mu, 0);
+  always_assert(ret == 0);
+
+  threadinfo *main_ti = threadinfo::make(threadinfo::TI_MAIN, -1);
+  main_ti->run();
+
+  initial_timestamp = timestamp();
+  tree = new Masstree::default_table;
+  tree->initialize(*main_ti);
+  printf("%s, %s, pin-threads %s, ", tree->name(), row_type::name(),
+         pinthreads ? "enabled" : "disabled");
+  if(logging){
+    printf("logging enabled\n");
+    log_init();
+    recover(main_ti);
+  } else {
+    printf("logging disabled\n");
+  }
+
+  // UDP threads, each with its own port.
+  if (udpthreads == 0)
+      printf("0 udp threads\n");
+  else if (udpthreads == 1)
+      printf("1 udp thread (port %d)\n", port);
+  else
+      printf("%d udp threads (ports %d-%d)\n", udpthreads, port, port + udpthreads - 1);
+  for(i = 0; i < udpthreads; i++){
+    threadinfo *ti = threadinfo::make(threadinfo::TI_PROCESS, i);
+    ret = ti->run(udp_threadfunc);
+    always_assert(ret == 0);
+  }
+
+  if (dotest) {
+      if (strcmp(dotest, "palm") == 0) {
+        runtest("palma", 1);
+        runtest("palmb", tcpthreads);
+      } else
+        runtest(dotest, tcpthreads);
+      tree->stats(stderr);
+      if (doprint)
+          tree->print(stdout, 0);
+      exit(0);
+  }
+
+  // TCP socket and threads
+
+  s = socket(AF_INET, SOCK_STREAM, 0);
+  always_assert(s >= 0);
+  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+  setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
+
+  struct sockaddr_in sin;
+  sin.sin_family = AF_INET;
+  sin.sin_addr.s_addr = INADDR_ANY;
+  sin.sin_port = htons(port);
+  ret = bind(s, (struct sockaddr *) &sin, sizeof(sin));
+  if (ret < 0) {
+      perror("bind");
+      exit(EXIT_FAILURE);
+  }
+
+  ret = listen(s, 100);
+  if (ret < 0) {
+      perror("listen");
+      exit(EXIT_FAILURE);
+  }
+
+  threadinfo **tcpti = new threadinfo *[tcpthreads];
+  tcp_thread_pipes = new int[tcpthreads * 2];
+  printf("%d tcp threads (port %d)\n", tcpthreads, port);
+  for(i = 0; i < tcpthreads; i++){
+    threadinfo *ti = threadinfo::make(threadinfo::TI_PROCESS, i);
+    ret = pipe(&tcp_thread_pipes[i * 2]);
+    always_assert(ret == 0);
+    ret = ti->run(tcp_threadfunc);
+    always_assert(ret == 0);
+    tcpti[i] = ti;
+  }
+  // Create a canceling thread.
+  ret = pipe(quit_pipe);
+  assert(ret == 0);
+  pthread_t tid;
+  pthread_create(&tid, NULL, canceling, NULL);
+
+  static int next = 0;
+  while(1){
+    int s1;
+    struct sockaddr_in sin1;
+    socklen_t sinlen = sizeof(sin1);
+
+    bzero(&sin1, sizeof(sin1));
+    s1 = accept(s, (struct sockaddr *) &sin1, &sinlen);
+    always_assert(s1 >= 0);
+    setsockopt(s1, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
+
+    // Complete handshake.
+    char buf[BUFSIZ];
+    ssize_t nr = read(s1, buf, BUFSIZ);
+    if (nr == -1) {
+        perror("read");
+    kill_connection:
+        close(s1);
+        continue;
+    }
+
+    msgpack::streaming_parser sp;
+    if (nr == 0 || sp.consume(buf, nr) != (size_t) nr
+        || !sp.result().is_a() || sp.result().size() < 2
+        || !sp.result()[1].is_i() || sp.result()[1].as_i() != Cmd_Handshake) {
+        fprintf(stderr, "failed handshake\n");
+        goto kill_connection;
+    }
+
+    int target_core = -1;
+    if (sp.result().size() >= 3 && sp.result()[2].is_o()
+        && sp.result()[2]["core"].is_i())
+        target_core = sp.result()[2]["core"].as_i();
+    if (target_core < 0 || target_core >= tcpthreads) {
+        target_core = next % tcpthreads;
+        ++next;
+    }
+
+    conninfo* ci = new conninfo;
+    ci->s = s1;
+    swap(ci->handshake, sp.result());
+
+    ssize_t w = write(tcp_thread_pipes[2*target_core + 1], &ci, sizeof(ci));
+    always_assert((size_t) w == sizeof(ci));
+  }
+}
+
+void
+catchint(int)
+{
+    go_quit = 1;
+    char cmd = 0;
+    // Does not matter if the write fails (when the pipe is full)
+    int r = write(quit_pipe[1], &cmd, sizeof(cmd));
+    (void)r;
+}
+
+inline const char *threadtype(int type) {
+  switch (type) {
+    case threadinfo::TI_MAIN:
+      return "main";
+    case threadinfo::TI_PROCESS:
+      return "process";
+    case threadinfo::TI_LOG:
+      return "log";
+    case threadinfo::TI_CHECKPOINT:
+      return "checkpoint";
+    default:
+      always_assert(0 && "Unknown threadtype");
+      break;
+  };
+}
+
+void *
+canceling(void *)
+{
+    char cmd;
+    int r = read(quit_pipe[0], &cmd, sizeof(cmd));
+    (void) r;
+    assert(r == sizeof(cmd) && cmd == 0);
+    // Cancel wake up checkpointing threads
+    pthread_mutex_lock(&checkpoint_mu);
+    pthread_cond_signal(&checkpoint_cond);
+    pthread_mutex_unlock(&checkpoint_mu);
+
+    pthread_t me = pthread_self();
+    fprintf(stderr, "\n");
+    // cancel outstanding threads. Checkpointing threads will exit safely
+    // when the checkpointing thread 0 sees go_quit, and don't need cancel
+    for (threadinfo *ti = threadinfo::allthreads; ti; ti = ti->next())
+        if (ti->purpose() != threadinfo::TI_MAIN
+            && ti->purpose() != threadinfo::TI_CHECKPOINT
+            && !pthread_equal(me, ti->threadid())) {
+            int r = pthread_cancel(ti->threadid());
+            always_assert(r == 0);
+        }
+
+    // join canceled threads
+    for (threadinfo *ti = threadinfo::allthreads; ti; ti = ti->next())
+        if (ti->purpose() != threadinfo::TI_MAIN
+            && !pthread_equal(me, ti->threadid())) {
+            fprintf(stderr, "joining thread %s:%d\n",
+                    threadtype(ti->purpose()), ti->index());
+            int r = pthread_join(ti->threadid(), 0);
+            always_assert(r == 0);
+        }
+    tree->stats(stderr);
+    exit(0);
+}
+
+void
+epochinc(int)
+{
+    globalepoch += 2;
+}
+
+// Return 1 if success, -1 if I/O error or protocol unmatch
+int handshake(Json& request, threadinfo& ti) {
+    always_assert(request.is_a() && request.size() >= 2
+                  && request[1].is_i() && request[1].as_i() == Cmd_Handshake
+                  && (request.size() == 2 || request[2].is_o()));
+    if (request.size() >= 2
+        && request[2]["maxkeylen"].is_i()
+        && request[2]["maxkeylen"].as_i() > MASSTREE_MAXKEYLEN) {
+        request[2] = false;
+        request[3] = "bad maxkeylen";
+        request.resize(4);
+    } else {
+        request[2] = true;
+        request[3] = ti.index();
+        request[4] = row_type::name();
+        request.resize(5);
+    }
+    request[1] = Cmd_Handshake + 1;
+    return request[2].as_b() ? 1 : -1;
+}
+
+// execute command, return result.
+int onego(query<row_type>& q, Json& request, Str request_str, threadinfo& ti) {
+    int command = request[1].as_i();
+    if (command == Cmd_Checkpoint) {
+        // force checkpoint
+        pthread_mutex_lock(&checkpoint_mu);
+        pthread_cond_broadcast(&checkpoint_cond);
+        pthread_mutex_unlock(&checkpoint_mu);
+        request.resize(2);
+    } else if (command == Cmd_Get) {
+        q.run_get(tree->table(), request, ti);
+    } else if (command == Cmd_Put && request.size() > 3
+               && (request.size() % 2) == 1) { // insert or update
+        Str key(request[2].as_s());
+        const Json* req = request.array_data() + 3;
+        const Json* end_req = request.end_array_data();
+        request[2] = q.run_put(tree->table(), request[2].as_s(),
+                               req, end_req, ti);
+        if (ti.logger() && request_str) {
+            // use the client's parsed version of the request
+            msgpack::parser mp(request_str.data());
+            mp.skip_array_size().skip_primitives(3);
+            ti.logger()->record(logcmd_put, q.query_times(), key, Str(mp.position(), request_str.end()));
+        } else if (ti.logger())
+            ti.logger()->record(logcmd_put, q.query_times(), key, req, end_req);
+        request.resize(3);
+    } else if (command == Cmd_Replace) { // insert or update
+        Str key(request[2].as_s()), value(request[3].as_s());
+        request[2] = q.run_replace(tree->table(), key, value, ti);
+        if (ti.logger()) // NB may block
+            ti.logger()->record(logcmd_replace, q.query_times(), key, value);
+        request.resize(3);
+    } else if (command == Cmd_Remove) { // remove
+        Str key(request[2].as_s());
+        bool removed = q.run_remove(tree->table(), key, ti);
+        if (removed && ti.logger()) // NB may block
+            ti.logger()->record(logcmd_remove, q.query_times(), key, Str());
+        request[2] = removed;
+        request.resize(3);
+    } else if (command == Cmd_Scan) {
+        q.run_scan(tree->table(), request, ti);
+    } else {
+        request[1] = -1;
+        request.resize(2);
+        return -1;
+    }
+    request[1] = command + 1;
+    return 1;
+}
+
+#if HAVE_SYS_EPOLL_H
+struct tcpfds {
+    int epollfd;
+
+    tcpfds(int pipefd) {
+        epollfd = epoll_create(10);
+        if (epollfd < 0) {
+            perror("epoll_create");
+            exit(EXIT_FAILURE);
+        }
+        struct epoll_event ev;
+        ev.events = EPOLLIN;
+        ev.data.ptr = (void *) 1;
+        int r = epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd, &ev);
+        always_assert(r == 0);
+    }
+
+    enum { max_events = 100 };
+    typedef struct epoll_event eventset[max_events];
+    int wait(eventset &es) {
+        return epoll_wait(epollfd, es, max_events, -1);
+    }
+
+    conn *event_conn(eventset &es, int i) const {
+        return (conn *) es[i].data.ptr;
+    }
+
+    void add(int fd, conn *c) {
+        struct epoll_event ev;
+        ev.events = EPOLLIN;
+        ev.data.ptr = c;
+        int r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
+        always_assert(r == 0);
+    }
+
+    void remove(int fd) {
+        int r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
+        always_assert(r == 0);
+    }
+};
+#else
+class tcpfds {
+    int nfds_;
+    fd_set rfds_;
+    std::vector<conn *> conns_;
+
+  public:
+    tcpfds(int pipefd)
+        : nfds_(pipefd + 1) {
+        always_assert(pipefd < FD_SETSIZE);
+        FD_ZERO(&rfds_);
+        FD_SET(pipefd, &rfds_);
+        conns_.resize(nfds_, 0);
+        conns_[pipefd] = (conn *) 1;
+    }
+
+    typedef fd_set eventset;
+    int wait(eventset &es) {
+        es = rfds_;
+        int r = select(nfds_, &es, 0, 0, 0);
+        return r > 0 ? nfds_ : r;
+    }
+
+    conn *event_conn(eventset &es, int i) const {
+        return FD_ISSET(i, &es) ? conns_[i] : 0;
+    }
+
+    void add(int fd, conn *c) {
+        always_assert(fd < FD_SETSIZE);
+        FD_SET(fd, &rfds_);
+        if (fd >= nfds_) {
+            nfds_ = fd + 1;
+            conns_.resize(nfds_, 0);
+        }
+        conns_[fd] = c;
+    }
+
+    void remove(int fd) {
+        always_assert(fd < FD_SETSIZE);
+        FD_CLR(fd, &rfds_);
+        if (fd == nfds_ - 1) {
+            while (nfds_ > 0 && !FD_ISSET(nfds_ - 1, &rfds_))
+                --nfds_;
+        }
+    }
+};
+#endif
+
+void prepare_thread(threadinfo *ti) {
+#if __linux__
+    if (pinthreads) {
+        cpu_set_t cs;
+        CPU_ZERO(&cs);
+        CPU_SET(cores[ti->index()], &cs);
+        always_assert(sched_setaffinity(0, sizeof(cs), &cs) == 0);
+    }
+#else
+    always_assert(!pinthreads && "pinthreads not supported\n");
+#endif
+    if (logging)
+        ti->set_logger(&logs->log(ti->index() % nlogger));
+}
+
+void* tcp_threadfunc(threadinfo* ti) {
+    prepare_thread(ti);
+
+    int myfd = tcp_thread_pipes[2 * ti->index()];
+    tcpfds sloop(myfd);
+    tcpfds::eventset events;
+    std::deque<conn*> ready;
+    query<row_type> q;
+
+    while (1) {
+        int nev = sloop.wait(events);
+        for (int i = 0; i < nev; i++)
+            if (conn *c = sloop.event_conn(events, i))
+                ready.push_back(c);
+
+        while (!ready.empty()) {
+            conn* c = ready.front();
+            ready.pop_front();
+
+            if (c == (conn *) 1) {
+                // new connections
+#define MAX_NEWCONN 100
+                conninfo* ci[MAX_NEWCONN];
+                ssize_t len = read(myfd, ci, sizeof(ci));
+                always_assert(len > 0 && len % sizeof(int) == 0);
+                for (int j = 0; j * sizeof(*ci) < (size_t) len; ++j) {
+                    struct conn *c = new conn(ci[j]->s);
+                    sloop.add(c->fd, c);
+                    int ret = handshake(ci[j]->handshake, *ti);
+                    msgpack::unparse(*c->kvout, ci[j]->handshake);
+                    kvflush(c->kvout);
+                    if (ret < 0) {
+                        sloop.remove(c->fd);
+                        delete c;
+                    }
+                    delete ci[j];
+                }
+            } else if (c) {
+                // Should not block as suggested by epoll
+                uint64_t xposition = c->xposition();
+                Json& request = c->receive();
+                int ret;
+                if (unlikely(!request))
+                    goto closed;
+                ti->rcu_start();
+                ret = onego(q, request, c->recent_string(xposition), *ti);
+                ti->rcu_stop();
+                msgpack::unparse(*c->kvout, request);
+                request.clear();
+                if (likely(ret >= 0)) {
+                    if (c->check(0))
+                        ready.push_back(c);
+                    else
+                        kvflush(c->kvout);
+                    continue;
+                }
+                printf("socket read error\n");
+            closed:
+                kvflush(c->kvout);
+                sloop.remove(c->fd);
+                delete c;
+            }
+        }
+    }
+    return 0;
+}
+
+// serve a client udp socket, in a dedicated thread
+void* udp_threadfunc(threadinfo* ti) {
+  int ret;
+  prepare_thread(ti);
+
+  struct sockaddr_in sin;
+  bzero(&sin, sizeof(sin));
+  sin.sin_family = AF_INET;
+  sin.sin_port = htons(port + ti->index());
+
+  int s = socket(AF_INET, SOCK_DGRAM, 0);
+  always_assert(s >= 0);
+  ret = bind(s, (struct sockaddr *) &sin, sizeof(sin));
+  always_assert(ret == 0 && "bind failed");
+  int sobuflen = 512*1024;
+  setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sobuflen, sizeof(sobuflen));
+
+  String buf = String::make_uninitialized(4096);
+  struct kvout *kvout = new_bufkvout();
+  msgpack::streaming_parser parser;
+  StringAccum sa;
+
+  query<row_type> q;
+  while(1){
+    struct sockaddr_in sin;
+    socklen_t sinlen = sizeof(sin);
+    ssize_t cc = recvfrom(s, const_cast<char*>(buf.data()), buf.length(),
+                          0, (struct sockaddr *) &sin, &sinlen);
+    if(cc < 0){
+      perror("udpgo read");
+      exit(EXIT_FAILURE);
+    }
+    kvout_reset(kvout);
+
+    parser.reset();
+    unsigned consumed = parser.consume(buf.data(), buf.length(), buf);
+
+    // Fail if we received a partial request
+    if (parser.success() && parser.result().is_a()) {
+        ti->rcu_start();
+        if (onego(q, parser.result(), Str(buf.data(), consumed), *ti) >= 0) {
+            sa.clear();
+            msgpack::unparser<StringAccum> cu(sa);
+            cu << parser.result();
+            cc = sendto(s, sa.data(), sa.length(), 0,
+                        (struct sockaddr*) &sin, sinlen);
+            always_assert(cc == (ssize_t) sa.length());
+        }
+        ti->rcu_stop();
+    } else
+      printf("onego failed\n");
+  }
+  return 0;
+}
+
+static String log_filename(const char* logdir, int logindex) {
+    struct stat sb;
+    int r = stat(logdir, &sb);
+    if (r < 0 && errno == ENOENT) {
+        r = mkdir(logdir, 0777);
+        if (r < 0) {
+            fprintf(stderr, "%s: %s\n", logdir, strerror(errno));
+            always_assert(0);
+        }
+    }
+
+    StringAccum sa;
+    sa.snprintf(strlen(logdir) + 24, "%s/kvd-log-%d", logdir, logindex);
+    return sa.take_string();
+}
+
+void log_init() {
+  int ret, i;
+
+  logs = logset::make(nlogger);
+  for (i = 0; i < nlogger; i++)
+      logs->log(i).initialize(log_filename(logdirs[i % logdirs.size()], i));
+
+  cks = (ckstate *)malloc(sizeof(ckstate) * nckthreads);
+  for (i = 0; i < nckthreads; i++) {
+    threadinfo *ti = threadinfo::make(threadinfo::TI_CHECKPOINT, i);
+    cks[i].state = CKState_Uninit;
+    cks[i].ti = ti;
+    ret = ti->run(conc_checkpointer);
+    always_assert(ret == 0);
+  }
+}
+
+// read a checkpoint, insert key/value pairs into tree.
+// must be followed by a read of the log!
+// since checkpoint is not consistent
+// with any one point in time.
+// returns the timestamp of the first log record that needs
+// to come from the log.
+kvepoch_t read_checkpoint(threadinfo *ti, const char *path) {
+    double t0 = now();
+
+    int fd = open(path, 0);
+    if(fd < 0){
+        printf("no %s\n", path);
+        return 0;
+    }
+    struct stat sb;
+    int ret = fstat(fd, &sb);
+    always_assert(ret == 0);
+    char *p = (char *) mmap(0, sb.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
+    always_assert(p != MAP_FAILED);
+    close(fd);
+
+    msgpack::parser par(String::make_stable(p, sb.st_size));
+    Json j;
+    par >> j;
+    std::cerr << j << "\n";
+    always_assert(j["generation"].is_i() && j["size"].is_i());
+    uint64_t gen = j["generation"].as_i();
+    uint64_t n = j["size"].as_i();
+    printf("reading checkpoint with %" PRIu64 " nodes\n", n);
+
+    // read data
+    for (uint64_t i = 0; i != n; ++i)
+        ckstate::insert(tree->table(), par, *ti);
+
+    munmap(p, sb.st_size);
+    double t1 = now();
+    printf("%.1f MB, %.2f sec, %.1f MB/sec\n",
+           sb.st_size / 1000000.0,
+           t1 - t0,
+           (sb.st_size / 1000000.0) / (t1 - t0));
+    return gen;
+}
+
+void
+waituntilphase(int phase)
+{
+  always_assert(pthread_mutex_lock(&rec_mu) == 0);
+  while (rec_state != phase)
+    always_assert(pthread_cond_wait(&rec_cond, &rec_mu) == 0);
+  always_assert(pthread_mutex_unlock(&rec_mu) == 0);
+}
+
+void
+inactive(void)
+{
+  always_assert(pthread_mutex_lock(&rec_mu) == 0);
+  rec_nactive --;
+  always_assert(pthread_cond_broadcast(&rec_cond) == 0);
+  always_assert(pthread_mutex_unlock(&rec_mu) == 0);
+}
+
+void recovercheckpoint(threadinfo *ti) {
+    waituntilphase(REC_CKP);
+    char path[256];
+    sprintf(path, "%s/kvd-ckp-%" PRId64 "-%d",
+            ckpdirs[ti->index() % ckpdirs.size()],
+            ckp_gen.value(), ti->index());
+    kvepoch_t gen = read_checkpoint(ti, path);
+    always_assert(ckp_gen == gen);
+    inactive();
+}
+
+void
+recphase(int nactive, int state)
+{
+  rec_nactive = nactive;
+  rec_state = state;
+  always_assert(pthread_cond_broadcast(&rec_cond) == 0);
+  while (rec_nactive)
+    always_assert(pthread_cond_wait(&rec_cond, &rec_mu) == 0);
+}
+
+// read the checkpoint file.
+// read each log file.
+// insert will ignore attempts to update with timestamps
+// less than what was in the entry from the checkpoint file.
+// so we don't have to do an explicit merge by time of the log files.
+void
+recover(threadinfo *)
+{
+  recovering = true;
+  // XXX: discard temporary checkpoint and ckp-gen files generated before crash
+
+  // get the generation of the checkpoint from ckp-gen, if any
+  char path[256];
+  sprintf(path, "%s/kvd-ckp-gen", ckpdirs[0]);
+  ckp_gen = 0;
+  rec_ckp_min_epoch = rec_ckp_max_epoch = 0;
+  int fd = open(path, O_RDONLY);
+  if (fd >= 0) {
+      Json ckpj = Json::parse(read_file_contents(fd));
+      close(fd);
+      if (ckpj && ckpj["kvdb_checkpoint"] && ckpj["generation"].is_number()) {
+          ckp_gen = ckpj["generation"].to_u64();
+          rec_ckp_min_epoch = ckpj["min_epoch"].to_u64();
+          rec_ckp_max_epoch = ckpj["max_epoch"].to_u64();
+          printf("recover from checkpoint %" PRIu64 " [%" PRIu64 ", %" PRIu64 "]\n", ckp_gen.value(), rec_ckp_min_epoch.value(), rec_ckp_max_epoch.value());
+      }
+  } else {
+    printf("no %s\n", path);
+  }
+  always_assert(pthread_mutex_lock(&rec_mu) == 0);
+
+  // recover from checkpoint, and set timestamp of the checkpoint
+  recphase(nckthreads, REC_CKP);
+
+  // find minimum maximum timestamp of entries in each log
+  rec_log_infos = new logreplay::info_type[nlogger];
+  recphase(nlogger, REC_LOG_TS);
+
+  // replay log entries, remove inconsistent entries, and append
+  // an empty log entry with minimum timestamp
+
+  // calculate log range
+
+  // Maximum epoch seen in the union of the logs and the checkpoint. (We
+  // don't commit a checkpoint until all logs are flushed past the
+  // checkpoint's max_epoch.)
+  kvepoch_t max_epoch = rec_ckp_max_epoch;
+  if (max_epoch)
+      max_epoch = max_epoch.next_nonzero();
+  for (logreplay::info_type *it = rec_log_infos;
+       it != rec_log_infos + nlogger; ++it)
+      if (it->last_epoch
+          && (!max_epoch || max_epoch < it->last_epoch))
+          max_epoch = it->last_epoch;
+
+  // Maximum first_epoch seen in the logs. Full log information is not
+  // available for epochs before max_first_epoch.
+  kvepoch_t max_first_epoch = 0;
+  for (logreplay::info_type *it = rec_log_infos;
+       it != rec_log_infos + nlogger; ++it)
+      if (it->first_epoch
+          && (!max_first_epoch || max_first_epoch < it->first_epoch))
+          max_first_epoch = it->first_epoch;
+
+  // Maximum epoch of all logged wake commands.
+  kvepoch_t max_wake_epoch = 0;
+  for (logreplay::info_type *it = rec_log_infos;
+       it != rec_log_infos + nlogger; ++it)
+      if (it->wake_epoch
+          && (!max_wake_epoch || max_wake_epoch < it->wake_epoch))
+          max_wake_epoch = it->wake_epoch;
+
+  // Minimum last_epoch seen in QUIESCENT logs.
+  kvepoch_t min_quiescent_last_epoch = 0;
+  for (logreplay::info_type *it = rec_log_infos;
+       it != rec_log_infos + nlogger; ++it)
+      if (it->quiescent
+          && (!min_quiescent_last_epoch || min_quiescent_last_epoch > it->last_epoch))
+          min_quiescent_last_epoch = it->last_epoch;
+
+  // If max_wake_epoch && min_quiescent_last_epoch <= max_wake_epoch, then a
+  // wake command was missed by at least one quiescent log. We can't replay
+  // anything at or beyond the minimum missed wake epoch. So record, for
+  // each log, the minimum wake command that at least one quiescent thread
+  // missed.
+  if (max_wake_epoch && min_quiescent_last_epoch <= max_wake_epoch)
+      rec_replay_min_quiescent_last_epoch = min_quiescent_last_epoch;
+  else
+      rec_replay_min_quiescent_last_epoch = 0;
+  recphase(nlogger, REC_LOG_ANALYZE_WAKE);
+
+  // Calculate upper bound of epochs to replay.
+  // This is the minimum of min_post_quiescent_wake_epoch (if any) and the
+  // last_epoch of all non-quiescent logs.
+  rec_replay_max_epoch = max_epoch;
+  for (logreplay::info_type *it = rec_log_infos;
+       it != rec_log_infos + nlogger; ++it) {
+      if (!it->quiescent
+          && it->last_epoch
+          && it->last_epoch < rec_replay_max_epoch)
+          rec_replay_max_epoch = it->last_epoch;
+      if (it->min_post_quiescent_wake_epoch
+          && it->min_post_quiescent_wake_epoch < rec_replay_max_epoch)
+          rec_replay_max_epoch = it->min_post_quiescent_wake_epoch;
+  }
+
+  // Calculate lower bound of epochs to replay.
+  rec_replay_min_epoch = rec_ckp_min_epoch;
+  // XXX what about max_first_epoch?
+
+  // Checks.
+  if (rec_ckp_min_epoch) {
+      always_assert(rec_ckp_min_epoch > max_first_epoch);
+      always_assert(rec_ckp_min_epoch < rec_replay_max_epoch);
+      always_assert(rec_ckp_max_epoch < rec_replay_max_epoch);
+      fprintf(stderr, "replay [%" PRIu64 ",%" PRIu64 ") from [%" PRIu64 ",%" PRIu64 ") into ckp [%" PRIu64 ",%" PRIu64 "]\n",
+              rec_replay_min_epoch.value(), rec_replay_max_epoch.value(),
+              max_first_epoch.value(), max_epoch.value(),
+              rec_ckp_min_epoch.value(), rec_ckp_max_epoch.value());
+  }
+
+  // Actually replay.
+  delete[] rec_log_infos;
+  rec_log_infos = 0;
+  recphase(nlogger, REC_LOG_REPLAY);
+
+  // done recovering
+  recphase(0, REC_DONE);
+#if !NDEBUG
+  // check that all delta markers have been recycled (leaving only remove
+  // markers and real values)
+  uint64_t deltas_created = 0, deltas_removed = 0;
+  for (threadinfo *ti = threadinfo::allthreads; ti; ti = ti->next()) {
+      deltas_created += ti->counter(tc_replay_create_delta);
+      deltas_removed += ti->counter(tc_replay_remove_delta);
+  }
+  if (deltas_created)
+      fprintf(stderr, "deltas created: %" PRIu64 ", removed: %" PRIu64 "\n", deltas_created, deltas_removed);
+  always_assert(deltas_created == deltas_removed);
+#endif
+
+  global_log_epoch = rec_replay_max_epoch.next_nonzero();
+
+  always_assert(pthread_mutex_unlock(&rec_mu) == 0);
+  recovering = false;
+  if (recovery_only)
+      exit(0);
+}
+
+void
+writecheckpoint(const char *path, ckstate *c, double t0)
+{
+  double t1 = now();
+  printf("memory phase: %" PRIu64 " nodes, %.2f sec\n", c->count, t1 - t0);
+
+  int fd = creat(path, 0666);
+  always_assert(fd >= 0);
+
+  // checkpoint file format, all msgpack:
+  //   {"generation": generation, "size": size, ...}
+  //   then `size` triples of key (string), timestmap (int), value (whatever)
+  Json j = Json().set("generation", ckp_gen.value())
+      .set("size", c->count)
+      .set("firstkey", c->startkey);
+  StringAccum sa;
+  msgpack::unparse(sa, j);
+  checked_write(fd, sa.data(), sa.length());
+  checked_write(fd, c->vals->buf, c->vals->n);
+
+  int ret = fsync(fd);
+  always_assert(ret == 0);
+  ret = close(fd);
+  always_assert(ret == 0);
+
+  double t2 = now();
+  c->bytes = c->vals->n;
+  printf("file phase (%s): %" PRIu64 " bytes, %.2f sec, %.1f MB/sec\n",
+         path,
+         c->bytes,
+         t2 - t1,
+         (c->bytes / 1000000.0) / (t2 - t1));
+}
+
+void
+conc_filecheckpoint(threadinfo *ti)
+{
+    ckstate *c = &cks[ti->index()];
+    c->vals = new_bufkvout();
+    double t0 = now();
+    tree->table().scan(c->startkey, true, *c, *ti);
+    char path[256];
+    sprintf(path, "%s/kvd-ckp-%" PRId64 "-%d",
+            ckpdirs[ti->index() % ckpdirs.size()],
+            ckp_gen.value(), ti->index());
+    writecheckpoint(path, c, t0);
+    c->count = 0;
+    free(c->vals);
+}
+
+static Json
+prepare_checkpoint(kvepoch_t min_epoch, int nckthreads, const Str *pv)
+{
+    Json j;
+    j.set("kvdb_checkpoint", true)
+        .set("min_epoch", min_epoch.value())
+        .set("max_epoch", global_log_epoch.value())
+        .set("generation", ckp_gen.value())
+        .set("nckthreads", nckthreads);
+
+    Json pvj;
+    for (int i = 1; i < nckthreads; ++i)
+        pvj.push_back(Json::make_string(pv[i].s, pv[i].len));
+    j.set("pivots", pvj);
+
+    return j;
+}
+
+static void
+commit_checkpoint(Json ckpj)
+{
+    // atomically commit a set of checkpoint files by incrementing
+    // the checkpoint generation on disk
+    char path[256];
+    sprintf(path, "%s/kvd-ckp-gen", ckpdirs[0]);
+    int r = atomic_write_file_contents(path, ckpj.unparse());
+    always_assert(r == 0);
+    fprintf(stderr, "kvd-ckp-%" PRIu64 " [%s,%s]: committed\n",
+            ckp_gen.value(), ckpj["min_epoch"].to_s().c_str(),
+            ckpj["max_epoch"].to_s().c_str());
+
+    // delete old checkpoint files
+    for (int i = 0; i < nckthreads; i++) {
+        char path[256];
+        sprintf(path, "%s/kvd-ckp-%" PRId64 "-%d",
+                ckpdirs[i % ckpdirs.size()],
+                ckp_gen.value() - 1, i);
+        unlink(path);
+    }
+}
+
+static kvepoch_t
+max_flushed_epoch()
+{
+    kvepoch_t mfe = 0, ge = global_log_epoch;
+    for (int i = 0; i < nlogger; ++i) {
+        loginfo& log = logs->log(i);
+        kvepoch_t fe = log.quiescent() ? ge : log.flushed_epoch();
+        if (!mfe || fe < mfe)
+            mfe = fe;
+    }
+    return mfe;
+}
+
+// concurrent periodic checkpoint
+void* conc_checkpointer(threadinfo* ti) {
+  recovercheckpoint(ti);
+  ckstate *c = &cks[ti->index()];
+  c->count = 0;
+  pthread_cond_init(&c->state_cond, NULL);
+  c->state = CKState_Ready;
+  while (recovering)
+    sleep(1);
+  if (checkpoint_interval <= 0)
+      return 0;
+  if (ti->index() == 0) {
+    for (int i = 1; i < nckthreads; i++)
+      while (cks[i].state != CKState_Ready)
+        ;
+    Str *pv = new Str[nckthreads + 1];
+    Json uncommitted_ckp;
+
+    while (1) {
+      struct timespec ts;
+      set_timespec(ts, now() + (uncommitted_ckp ? 0.25 : checkpoint_interval));
+
+      pthread_mutex_lock(&checkpoint_mu);
+      if (!go_quit)
+        pthread_cond_timedwait(&checkpoint_cond, &checkpoint_mu, &ts);
+      if (go_quit) {
+          for (int i = 0; i < nckthreads; i++) {
+              cks[i].state = CKState_Quit;
+              pthread_cond_signal(&cks[i].state_cond);
+          }
+          pthread_mutex_unlock(&checkpoint_mu);
+          break;
+      }
+      pthread_mutex_unlock(&checkpoint_mu);
+
+      if (uncommitted_ckp) {
+          kvepoch_t mfe = max_flushed_epoch();
+          if (!mfe || mfe > uncommitted_ckp["max_epoch"].to_u64()) {
+              commit_checkpoint(uncommitted_ckp);
+              uncommitted_ckp = Json();
+          }
+          continue;
+      }
+
+      double t0 = now();
+      ti->rcu_start();
+      for (int i = 0; i < nckthreads + 1; i++)
+        pv[i].assign(NULL, 0);
+      tree->findpivots(pv, nckthreads + 1);
+      ti->rcu_stop();
+
+      kvepoch_t min_epoch = global_log_epoch;
+      pthread_mutex_lock(&checkpoint_mu);
+      ckp_gen = ckp_gen.next_nonzero();
+      for (int i = 0; i < nckthreads; i++) {
+          cks[i].startkey = pv[i];
+          cks[i].endkey = (i == nckthreads - 1 ? Str() : pv[i + 1]);
+          cks[i].state = CKState_Go;
+          pthread_cond_signal(&cks[i].state_cond);
+      }
+      pthread_mutex_unlock(&checkpoint_mu);
+
+      ti->rcu_start();
+      conc_filecheckpoint(ti);
+      ti->rcu_stop();
+
+      cks[0].state = CKState_Ready;
+      uint64_t bytes = cks[0].bytes;
+      pthread_mutex_lock(&checkpoint_mu);
+      for (int i = 1; i < nckthreads; i++) {
+        while (cks[i].state != CKState_Ready)
+          pthread_cond_wait(&cks[i].state_cond, &checkpoint_mu);
+        bytes += cks[i].bytes;
+      }
+      pthread_mutex_unlock(&checkpoint_mu);
+
+      uncommitted_ckp = prepare_checkpoint(min_epoch, nckthreads, pv);
+
+      for (int i = 0; i < nckthreads + 1; i++)
+        if (pv[i].s)
+          free((void *)pv[i].s);
+      double t = now() - t0;
+      fprintf(stderr, "kvd-ckp-%" PRIu64 " [%s,%s]: prepared (%.2f sec, %" PRIu64 " MB, %" PRIu64 " MB/sec)\n",
+              ckp_gen.value(), uncommitted_ckp["min_epoch"].to_s().c_str(),
+              uncommitted_ckp["max_epoch"].to_s().c_str(),
+              t, bytes / (1 << 20), (uint64_t)(bytes / t) >> 20);
+    }
+  } else {
+    while(1) {
+      pthread_mutex_lock(&checkpoint_mu);
+      while (c->state != CKState_Go && c->state != CKState_Quit)
+        pthread_cond_wait(&c->state_cond, &checkpoint_mu);
+      if (c->state == CKState_Quit) {
+        pthread_mutex_unlock(&checkpoint_mu);
+        break;
+      }
+      pthread_mutex_unlock(&checkpoint_mu);
+
+      ti->rcu_start();
+      conc_filecheckpoint(ti);
+      ti->rcu_stop();
+
+      pthread_mutex_lock(&checkpoint_mu);
+      c->state = CKState_Ready;
+      pthread_cond_signal(&c->state_cond);
+      pthread_mutex_unlock(&checkpoint_mu);
+    }
+  }
+  return 0;
+}
diff --git a/silo/masstree/mttest.cc b/silo/masstree/mttest.cc
new file mode 100644 (file)
index 0000000..91f87b1
--- /dev/null
@@ -0,0 +1,1358 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+// -*- mode: c++ -*-
+// mttest: key/value tester
+//
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/select.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/utsname.h>
+#include <limits.h>
+#if HAVE_NUMA_H
+#include <numa.h>
+#endif
+#if HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#endif
+#if HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+#if __linux__
+#include <asm-generic/mman.h>
+#endif
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+#include <signal.h>
+#include <errno.h>
+#ifdef __linux__
+#include <malloc.h>
+#endif
+#include "nodeversion.hh"
+#include "kvstats.hh"
+#include "query_masstree.hh"
+#include "masstree_tcursor.hh"
+#include "masstree_insert.hh"
+#include "masstree_remove.hh"
+#include "masstree_scan.hh"
+#include "timestamp.hh"
+#include "json.hh"
+#include "kvtest.hh"
+#include "kvrandom.hh"
+#include "kvrow.hh"
+#include "kvio.hh"
+#include "clp.h"
+#include <algorithm>
+#include <numeric>
+
+static std::vector<int> cores;
+volatile bool timeout[2] = {false, false};
+double duration[2] = {10, 0};
+// Do not start timer until asked
+static bool lazy_timer = false;
+int kvtest_first_seed = 31949;
+uint64_t test_limit = ~uint64_t(0);
+static Json test_param;
+
+bool quiet = false;
+bool print_table = false;
+static const char *gid = NULL;
+
+// all default to the number of cores
+static int udpthreads = 0;
+static int tcpthreads = 0;
+
+static bool tree_stats = false;
+static bool json_stats = false;
+static bool pinthreads = false;
+volatile uint64_t globalepoch = 1;     // global epoch, updated by main thread regularly
+kvepoch_t global_log_epoch = 0;
+static int port = 2117;
+static int rscale_ncores = 0;
+
+#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
+struct mttest_numainfo {
+    long long free;
+    long long size;
+};
+std::vector<mttest_numainfo> numa;
+#endif
+
+volatile bool recovering = false; // so don't add log entries, and free old value immediately
+kvtimestamp_t initial_timestamp;
+
+static const char *threadcounter_names[(int) tc_max];
+
+/* running local tests */
+void test_timeout(int) {
+    size_t n;
+    for (n = 0; n < arraysize(timeout) && timeout[n]; ++n)
+        /* do nothing */;
+    if (n < arraysize(timeout)) {
+        timeout[n] = true;
+        if (n + 1 < arraysize(timeout) && duration[n + 1])
+            xalarm(duration[n + 1]);
+    }
+}
+
+template <typename T>
+struct kvtest_client {
+    kvtest_client()
+        : limit_(test_limit), ncores_(udpthreads), kvo_() {
+    }
+    ~kvtest_client() {
+        if (kvo_)
+            free_kvout(kvo_);
+    }
+
+    int nthreads() const {
+        return udpthreads;
+    }
+    int id() const {
+        return ti_->index();
+    }
+    void set_table(T *table, threadinfo *ti) {
+        table_ = table;
+        ti_ = ti;
+    }
+    void reset(const String &test, int trial) {
+        report_ = Json().set("table", T().name())
+            .set("test", test).set("trial", trial)
+            .set("thread", ti_->index());
+    }
+    static void start_timer() {
+        always_assert(lazy_timer && "Cannot start timer without lazy_timer option");
+        always_assert(duration[0] && "Must specify timeout[0]");
+        xalarm(duration[0]);
+    }
+
+    bool timeout(int which) const {
+        return ::timeout[which];
+    }
+    uint64_t limit() const {
+        return limit_;
+    }
+    Json param(const String& name) const {
+        return test_param[name];
+    }
+
+    int ncores() const {
+        return ncores_;
+    }
+    double now() const {
+        return ::now();
+    }
+    int ruscale_partsz() const {
+        return (140 * 1000000) / 16;
+    }
+    int ruscale_init_part_no() const {
+        return ti_->index();
+    }
+    long nseqkeys() const {
+        return 16 * ruscale_partsz();
+    }
+
+    void get(long ikey);
+    bool get_sync(const Str &key);
+    bool get_sync(const Str &key, Str &value);
+    bool get_sync(long ikey) {
+        quick_istr key(ikey);
+        return get_sync(key.string());
+    }
+    bool get_sync_key16(long ikey) {
+        quick_istr key(ikey, 16);
+        return get_sync(key.string());
+    }
+    void get_check(const Str &key, const Str &expected);
+    void get_check(const char *key, const char *expected) {
+        get_check(Str(key), Str(expected));
+    }
+    void get_check(long ikey, long iexpected) {
+        quick_istr key(ikey), expected(iexpected);
+        get_check(key.string(), expected.string());
+    }
+    void get_check(const Str &key, long iexpected) {
+        quick_istr expected(iexpected);
+        get_check(key, expected.string());
+    }
+    void get_check_key8(long ikey, long iexpected) {
+        quick_istr key(ikey, 8), expected(iexpected);
+        get_check(key.string(), expected.string());
+    }
+    void get_col_check(const Str &key, int col, const Str &value);
+    void get_col_check(long ikey, int col, long ivalue) {
+        quick_istr key(ikey), value(ivalue);
+        get_col_check(key.string(), col, value.string());
+    }
+    void get_col_check_key10(long ikey, int col, long ivalue) {
+        quick_istr key(ikey, 10), value(ivalue);
+        get_col_check(key.string(), col, value.string());
+    }
+    //void many_get_check(int nk, long ikey[], long iexpected[]);
+
+    void scan_sync(const Str &firstkey, int n,
+                   std::vector<Str> &keys, std::vector<Str> &values);
+    void rscan_sync(const Str &firstkey, int n,
+                    std::vector<Str> &keys, std::vector<Str> &values);
+
+    void put(const Str &key, const Str &value);
+    void put(const char *key, const char *value) {
+        put(Str(key), Str(value));
+    }
+    void put(long ikey, long ivalue) {
+        quick_istr key(ikey), value(ivalue);
+        put(key.string(), value.string());
+    }
+    void put(const Str &key, long ivalue) {
+        quick_istr value(ivalue);
+        put(key, value.string());
+    }
+    void put_key8(long ikey, long ivalue) {
+        quick_istr key(ikey, 8), value(ivalue);
+        put(key.string(), value.string());
+    }
+    void put_key16(long ikey, long ivalue) {
+        quick_istr key(ikey, 16), value(ivalue);
+        put(key.string(), value.string());
+    }
+    void put_col(const Str &key, int col, const Str &value);
+    void put_col(long ikey, int col, long ivalue) {
+        quick_istr key(ikey), value(ivalue);
+        put_col(key.string(), col, value.string());
+    }
+    void put_col_key10(long ikey, int col, long ivalue) {
+        quick_istr key(ikey, 10), value(ivalue);
+        put_col(key.string(), col, value.string());
+    }
+
+    void remove(const Str &key);
+    void remove(long ikey) {
+        quick_istr key(ikey);
+        remove(key.string());
+    }
+    void remove_key8(long ikey) {
+        quick_istr key(ikey, 8);
+        remove(key.string());
+    }
+    void remove_key16(long ikey) {
+        quick_istr key(ikey, 16);
+        remove(key.string());
+    }
+    bool remove_sync(const Str &key);
+    bool remove_sync(long ikey) {
+        quick_istr key(ikey);
+        return remove_sync(key.string());
+    }
+
+    void puts_done() {
+    }
+    void wait_all() {
+    }
+    void rcu_quiesce() {
+        uint64_t e = timestamp() >> 16;
+        if (e != globalepoch)
+            globalepoch = e;
+        ti_->rcu_quiesce();
+    }
+    String make_message(lcdf::StringAccum &sa) const;
+    void notice(const char *fmt, ...);
+    void fail(const char *fmt, ...);
+    const Json& report(const Json& x) {
+        return report_.merge(x);
+    }
+    void finish() {
+        Json counters;
+        for (int i = 0; i < tc_max; ++i)
+            if (uint64_t c = ti_->counter(threadcounter(i)))
+                counters.set(threadcounter_names[i], c);
+        if (counters)
+            report_.set("counters", counters);
+        if (!quiet)
+            fprintf(stderr, "%d: %s\n", ti_->index(), report_.unparse().c_str());
+    }
+
+    T *table_;
+    threadinfo *ti_;
+    query<row_type> q_[1];
+    kvrandom_lcg_nr rand;
+    uint64_t limit_;
+    Json report_;
+    int ncores_;
+    kvout *kvo_;
+
+  private:
+    void output_scan(const Json& req, std::vector<Str>& keys, std::vector<Str>& values) const;
+};
+
+static volatile int kvtest_printing;
+
+template <typename T> inline void kvtest_print(const T &table, FILE *f, int indent, threadinfo *ti) {
+    // only print out the tree from the first failure
+    while (!bool_cmpxchg((int *) &kvtest_printing, 0, ti->index() + 1))
+        /* spin */;
+    table.print(f, indent);
+}
+
+template <typename T> inline void kvtest_json_stats(T& table, Json& j, threadinfo& ti) {
+    table.json_stats(j, ti);
+}
+
+template <typename T>
+void kvtest_client<T>::get(long ikey) {
+    quick_istr key(ikey);
+    Str val;
+    (void) q_[0].run_get1(table_->table(), key.string(), 0, val, *ti_);
+}
+
+template <typename T>
+bool kvtest_client<T>::get_sync(const Str& key) {
+    Str val;
+    return q_[0].run_get1(table_->table(), key, 0, val, *ti_);
+}
+
+template <typename T>
+bool kvtest_client<T>::get_sync(const Str &key, Str &value) {
+    return q_[0].run_get1(table_->table(), key, 0, value, *ti_);
+}
+
+template <typename T>
+void kvtest_client<T>::get_check(const Str &key, const Str &expected) {
+    Str val;
+    if (!q_[0].run_get1(table_->table(), key, 0, val, *ti_))
+        fail("get(%.*s) failed (expected %.*s)\n", key.len, key.s,
+             expected.len, expected.s);
+    else if (expected != val)
+        fail("get(%.*s) returned unexpected value %.*s (expected %.*s)\n",
+             key.len, key.s, std::min(val.len, 40), val.s,
+             expected.len, expected.s);
+}
+
+template <typename T>
+void kvtest_client<T>::get_col_check(const Str &key, int col,
+                                     const Str &expected) {
+    Str val;
+    if (!q_[0].run_get1(table_->table(), key, col, val, *ti_))
+        fail("get.%d(%.*s) failed (expected %.*s)\n",
+             col, key.len, key.s, expected.len, expected.s);
+    else if (expected != val)
+        fail("get.%d(%.*s) returned unexpected value %.*s (expected %.*s)\n",
+             col, key.len, key.s, std::min(val.len, 40), val.s,
+             expected.len, expected.s);
+}
+
+/*template <typename T>
+void kvtest_client<T>::many_get_check(int nk, long ikey[], long iexpected[]) {
+    std::vector<quick_istr> ka(2*nk, quick_istr());
+    for(int i = 0; i < nk; i++){
+      ka[i].set(ikey[i]);
+      ka[i+nk].set(iexpected[i]);
+      q_[i].begin_get1(ka[i].string());
+    }
+    table_->many_get(q_, nk, *ti_);
+    for(int i = 0; i < nk; i++){
+      Str val = q_[i].get1_value();
+      if (ka[i+nk] != val){
+        printf("get(%ld) returned unexpected value %.*s (expected %ld)\n",
+             ikey[i], std::min(val.len, 40), val.s, iexpected[i]);
+        exit(1);
+      }
+    }
+}*/
+
+template <typename T>
+void kvtest_client<T>::scan_sync(const Str &firstkey, int n,
+                                 std::vector<Str> &keys,
+                                 std::vector<Str> &values) {
+    Json req = Json::array(0, 0, firstkey, n);
+    q_[0].run_scan(table_->table(), req, *ti_);
+    output_scan(req, keys, values);
+}
+
+template <typename T>
+void kvtest_client<T>::rscan_sync(const Str &firstkey, int n,
+                                  std::vector<Str> &keys,
+                                  std::vector<Str> &values) {
+    Json req = Json::array(0, 0, firstkey, n);
+    q_[0].run_rscan(table_->table(), req, *ti_);
+    output_scan(req, keys, values);
+}
+
+template <typename T>
+void kvtest_client<T>::output_scan(const Json& req, std::vector<Str>& keys,
+                                   std::vector<Str>& values) const {
+    keys.clear();
+    values.clear();
+    for (int i = 2; i != req.size(); i += 2) {
+        keys.push_back(req[i].as_s());
+        values.push_back(req[i + 1].as_s());
+    }
+}
+
+template <typename T>
+void kvtest_client<T>::put(const Str &key, const Str &value) {
+    q_[0].run_replace(table_->table(), key, value, *ti_);
+}
+
+template <typename T>
+void kvtest_client<T>::put_col(const Str &key, int col, const Str &value) {
+#if !MASSTREE_ROW_TYPE_STR
+    if (!kvo_)
+        kvo_ = new_kvout(-1, 2048);
+    Json x[2] = {Json(col), Json(String::make_stable(value))};
+    q_[0].run_put(table_->table(), key, &x[0], &x[2], *ti_);
+#else
+    (void) key, (void) col, (void) value;
+    assert(0);
+#endif
+}
+
+template <typename T> inline bool kvtest_remove(kvtest_client<T> &client, const Str &key) {
+    return client.q_[0].run_remove(client.table_->table(), key, *client.ti_);
+}
+
+template <typename T>
+void kvtest_client<T>::remove(const Str &key) {
+    (void) kvtest_remove(*this, key);
+}
+
+template <typename T>
+bool kvtest_client<T>::remove_sync(const Str &key) {
+    return kvtest_remove(*this, key);
+}
+
+template <typename T>
+String kvtest_client<T>::make_message(lcdf::StringAccum &sa) const {
+    const char *begin = sa.begin();
+    while (begin != sa.end() && isspace((unsigned char) *begin))
+        ++begin;
+    String s = String(begin, sa.end());
+    if (!s.empty() && s.back() != '\n')
+        s += '\n';
+    return s;
+}
+
+template <typename T>
+void kvtest_client<T>::notice(const char *fmt, ...) {
+    va_list val;
+    va_start(val, fmt);
+    String m = make_message(lcdf::StringAccum().vsnprintf(500, fmt, val));
+    va_end(val);
+    if (m && !quiet)
+        fprintf(stderr, "%d: %s", ti_->index(), m.c_str());
+}
+
+template <typename T>
+void kvtest_client<T>::fail(const char *fmt, ...) {
+    static nodeversion failing_lock(false);
+    static nodeversion fail_message_lock(false);
+    static String fail_message;
+
+    va_list val;
+    va_start(val, fmt);
+    String m = make_message(lcdf::StringAccum().vsnprintf(500, fmt, val));
+    va_end(val);
+    if (!m)
+        m = "unknown failure";
+
+    fail_message_lock.lock();
+    if (fail_message != m) {
+        fail_message = m;
+        fprintf(stderr, "%d: %s", ti_->index(), m.c_str());
+    }
+    fail_message_lock.unlock();
+
+    failing_lock.lock();
+    fprintf(stdout, "%d: %s", ti_->index(), m.c_str());
+    kvtest_print(*table_, stdout, 0, ti_);
+
+    always_assert(0);
+}
+
+
+static const char *current_test_name;
+static int current_trial;
+static FILE *test_output_file;
+static pthread_mutex_t subtest_mutex;
+static pthread_cond_t subtest_cond;
+
+#define TESTRUNNER_CLIENT_TYPE kvtest_client<Masstree::default_table>&
+#include "testrunner.hh"
+
+MAKE_TESTRUNNER(rw1, kvtest_rw1(client));
+// MAKE_TESTRUNNER(palma, kvtest_palma(client));
+// MAKE_TESTRUNNER(palmb, kvtest_palmb(client));
+MAKE_TESTRUNNER(rw1fixed, kvtest_rw1fixed(client));
+MAKE_TESTRUNNER(rw1long, kvtest_rw1long(client));
+MAKE_TESTRUNNER(rw1puts, kvtest_rw1puts(client));
+MAKE_TESTRUNNER(rw2, kvtest_rw2(client));
+MAKE_TESTRUNNER(rw2fixed, kvtest_rw2fixed(client));
+MAKE_TESTRUNNER(rw2g90, kvtest_rw2g90(client));
+MAKE_TESTRUNNER(rw2fixedg90, kvtest_rw2fixedg90(client));
+MAKE_TESTRUNNER(rw2g98, kvtest_rw2g98(client));
+MAKE_TESTRUNNER(rw2fixedg98, kvtest_rw2fixedg98(client));
+MAKE_TESTRUNNER(rw3, kvtest_rw3(client));
+MAKE_TESTRUNNER(rw4, kvtest_rw4(client));
+MAKE_TESTRUNNER(rw4fixed, kvtest_rw4fixed(client));
+MAKE_TESTRUNNER(wd1, kvtest_wd1(10000000, 1, client));
+MAKE_TESTRUNNER(wd1m1, kvtest_wd1(100000000, 1, client));
+MAKE_TESTRUNNER(wd1m2, kvtest_wd1(1000000000, 4, client));
+MAKE_TESTRUNNER(same, kvtest_same(client));
+MAKE_TESTRUNNER(rwsmall24, kvtest_rwsmall24(client));
+MAKE_TESTRUNNER(rwsep24, kvtest_rwsep24(client));
+MAKE_TESTRUNNER(wscale, kvtest_wscale(client));
+MAKE_TESTRUNNER(ruscale_init, kvtest_ruscale_init(client));
+MAKE_TESTRUNNER(rscale, if (client.ti_->index() < ::rscale_ncores) kvtest_rscale(client));
+MAKE_TESTRUNNER(uscale, kvtest_uscale(client));
+MAKE_TESTRUNNER(bdb, kvtest_bdb(client));
+MAKE_TESTRUNNER(wcol1, kvtest_wcol1at(client, client.id() % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(rcol1, kvtest_rcol1at(client, client.id() % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(wcol1o1, kvtest_wcol1at(client, (client.id() + 1) % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(rcol1o1, kvtest_rcol1at(client, (client.id() + 1) % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(wcol1o2, kvtest_wcol1at(client, (client.id() + 2) % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(rcol1o2, kvtest_rcol1at(client, (client.id() + 2) % 24, kvtest_first_seed + client.id() % 48, 5000000));
+MAKE_TESTRUNNER(scan1, kvtest_scan1(client, 0));
+MAKE_TESTRUNNER(scan1q80, kvtest_scan1(client, 0.8));
+MAKE_TESTRUNNER(rscan1, kvtest_rscan1(client, 0));
+MAKE_TESTRUNNER(rscan1q80, kvtest_rscan1(client, 0.8));
+MAKE_TESTRUNNER(splitremove1, kvtest_splitremove1(client));
+MAKE_TESTRUNNER(url, kvtest_url(client));
+
+
+enum {
+    test_thread_initialize = 1,
+    test_thread_destroy = 2,
+    test_thread_stats = 3
+};
+
+template <typename T>
+struct test_thread {
+    test_thread(threadinfo* ti) {
+        client_.set_table(table_, ti);
+        client_.ti_->rcu_start();
+    }
+    ~test_thread() {
+        client_.ti_->rcu_stop();
+    }
+    static void setup(threadinfo* ti, int action) {
+        if (action == test_thread_initialize) {
+            assert(!table_);
+            table_ = new T;
+            table_->initialize(*ti);
+        } else if (action == test_thread_destroy) {
+            assert(table_);
+            delete table_;
+            table_ = 0;
+        } else if (action == test_thread_stats) {
+            assert(table_);
+            table_->stats(test_output_file);
+        }
+    }
+    static void* go(threadinfo* ti) {
+        assert(table_);
+#if __linux__
+        if (pinthreads) {
+            cpu_set_t cs;
+            CPU_ZERO(&cs);
+            CPU_SET(cores[ti->index()], &cs);
+            int r = sched_setaffinity(0, sizeof(cs), &cs);
+            always_assert(r == 0);
+        }
+#else
+        always_assert(!pinthreads && "pinthreads not supported\n");
+#endif
+
+        test_thread<T> tt(ti);
+        if (fetch_and_add(&active_threads_, 1) == 0)
+            tt.ready_timeouts();
+        String test = ::current_test_name;
+        int subtestno = 0;
+        for (int pos = 0; pos < test.length(); ) {
+            int comma = test.find_left(',', pos);
+            comma = (comma < 0 ? test.length() : comma);
+            String subtest = test.substr(pos, comma - pos), tname;
+            testrunner* tr = testrunner::find(subtest);
+            tname = (subtest == test ? subtest : test + String("@") + String(subtestno));
+            tt.client_.reset(tname, ::current_trial);
+            if (tr)
+                tr->run(tt.client_);
+            else
+                tt.client_.fail("unknown test %s", subtest.c_str());
+            if (comma == test.length())
+                break;
+            pthread_mutex_lock(&subtest_mutex);
+            if (fetch_and_add(&active_threads_, -1) == 1) {
+                pthread_cond_broadcast(&subtest_cond);
+                tt.ready_timeouts();
+            } else
+                pthread_cond_wait(&subtest_cond, &subtest_mutex);
+            fprintf(test_output_file, "%s\n", tt.client_.report_.unparse().c_str());
+            pthread_mutex_unlock(&subtest_mutex);
+            fetch_and_add(&active_threads_, 1);
+            pos = comma + 1;
+            ++subtestno;
+        }
+        int at = fetch_and_add(&active_threads_, -1);
+        if (at == 1 && print_table)
+            kvtest_print(*table_, stdout, 0, tt.client_.ti_);
+        if (at == 1 && json_stats) {
+            Json j;
+            kvtest_json_stats(*table_, j, *tt.client_.ti_);
+            if (j) {
+                fprintf(stderr, "%s\n", j.unparse(Json::indent_depth(1).tab_width(2).newline_terminator(true)).c_str());
+                tt.client_.report_.merge(j);
+            }
+        }
+        fprintf(test_output_file, "%s\n", tt.client_.report_.unparse().c_str());
+        return 0;
+    }
+    void ready_timeouts() {
+        for (size_t i = 0; i < arraysize(timeout); ++i)
+            timeout[i] = false;
+        if (!lazy_timer && duration[0])
+            xalarm(duration[0]);
+    }
+    static T *table_;
+    static unsigned active_threads_;
+    kvtest_client<T> client_;
+};
+template <typename T> T *test_thread<T>::table_;
+template <typename T> unsigned test_thread<T>::active_threads_;
+
+typedef test_thread<Masstree::default_table> masstree_test_thread;
+
+static struct {
+    const char *treetype;
+    void* (*go_func)(threadinfo*);
+    void (*setup_func)(threadinfo*, int);
+} test_thread_map[] = {
+    { "masstree", masstree_test_thread::go, masstree_test_thread::setup },
+    { "mass", masstree_test_thread::go, masstree_test_thread::setup },
+    { "mbtree", masstree_test_thread::go, masstree_test_thread::setup },
+    { "mb", masstree_test_thread::go, masstree_test_thread::setup },
+    { "m", masstree_test_thread::go, masstree_test_thread::setup }
+};
+
+
+void runtest(int nthreads, void* (*func)(threadinfo*)) {
+    std::vector<threadinfo *> tis;
+    for (int i = 0; i < nthreads; ++i)
+        tis.push_back(threadinfo::make(threadinfo::TI_PROCESS, i));
+    signal(SIGALRM, test_timeout);
+    for (int i = 0; i < nthreads; ++i) {
+        int r = tis[i]->run(func);
+        always_assert(r == 0);
+    }
+    for (int i = 0; i < nthreads; ++i)
+        pthread_join(tis[i]->threadid(), 0);
+}
+
+
+static const char * const kvstats_name[] = {
+    "ops_per_sec", "puts_per_sec", "gets_per_sec", "scans_per_sec"
+};
+
+static Json experiment_stats;
+
+void *stat_collector(void *arg) {
+    int p = (int) (intptr_t) arg;
+    FILE *f = fdopen(p, "r");
+    char buf[8192];
+    while (fgets(buf, sizeof(buf), f)) {
+        Json result = Json::parse(buf);
+        if (result && result["table"] && result["test"]) {
+            String key = result["test"].to_s() + "/" + result["table"].to_s()
+                + "/" + result["trial"].to_s();
+            Json &thisex = experiment_stats.get_insert(key);
+            thisex[result["thread"].to_i()] = result;
+        } else
+            fprintf(stderr, "%s\n", buf);
+    }
+    fclose(f);
+    return 0;
+}
+
+
+/* main loop */
+
+enum { clp_val_normalize = Clp_ValFirstUser, clp_val_suffixdouble };
+enum { opt_pin = 1, opt_port, opt_duration,
+       opt_test, opt_test_name, opt_threads, opt_trials, opt_quiet, opt_print,
+       opt_normalize, opt_limit, opt_notebook, opt_compare, opt_no_run,
+       opt_lazy_timer, opt_gid, opt_tree_stats, opt_rscale_ncores, opt_cores,
+       opt_stats, opt_help };
+static const Clp_Option options[] = {
+    { "pin", 'p', opt_pin, 0, Clp_Negate },
+    { "port", 0, opt_port, Clp_ValInt, 0 },
+    { "duration", 'd', opt_duration, Clp_ValDouble, 0 },
+    { "lazy-timer", 0, opt_lazy_timer, 0, 0 },
+    { "limit", 'l', opt_limit, clp_val_suffixdouble, 0 },
+    { "normalize", 0, opt_normalize, clp_val_normalize, Clp_Negate },
+    { "test", 0, opt_test, Clp_ValString, 0 },
+    { "rscale_ncores", 'r', opt_rscale_ncores, Clp_ValInt, 0 },
+    { "test-rw1", 0, opt_test_name, 0, 0 },
+    { "test-rw2", 0, opt_test_name, 0, 0 },
+    { "test-rw3", 0, opt_test_name, 0, 0 },
+    { "test-rw4", 0, opt_test_name, 0, 0 },
+    { "test-rd1", 0, opt_test_name, 0, 0 },
+    { "threads", 'j', opt_threads, Clp_ValInt, 0 },
+    { "trials", 'T', opt_trials, Clp_ValInt, 0 },
+    { "quiet", 'q', opt_quiet, 0, Clp_Negate },
+    { "print", 0, opt_print, 0, Clp_Negate },
+    { "notebook", 'b', opt_notebook, Clp_ValString, Clp_Negate },
+    { "gid", 'g', opt_gid, Clp_ValString, 0 },
+    { "tree-stats", 0, opt_tree_stats, 0, 0 },
+    { "stats", 0, opt_stats, 0, 0 },
+    { "compare", 'c', opt_compare, Clp_ValString, 0 },
+    { "cores", 0, opt_cores, Clp_ValString, 0 },
+    { "no-run", 'n', opt_no_run, 0, 0 },
+    { "help", 0, opt_help, 0, 0 }
+};
+
+static void help() {
+    printf("Masstree-beta mttest\n\
+Usage: mttest [-jTHREADS] [OPTIONS] [PARAM=VALUE...] TEST...\n\
+       mttest -n -c TESTNAME...\n\
+\n\
+Options:\n\
+  -j, --threads=THREADS    Run with THREADS threads (default %d).\n\
+  -p, --pin                Pin each thread to its own core.\n\
+  -T, --trials=TRIALS      Run each test TRIALS times.\n\
+  -q, --quiet              Do not generate verbose and Gnuplot output.\n\
+  -l, --limit=LIMIT        Limit relevant tests to LIMIT operations.\n\
+  -d, --duration=TIME      Limit relevant tests to TIME seconds.\n\
+  -b, --notebook=FILE      Record JSON results in FILE (notebook-mttest.json).\n\
+      --no-notebook        Do not record JSON results.\n\
+\n\
+  -n, --no-run             Do not run new tests.\n\
+  -c, --compare=EXPERIMENT Generated plot compares to EXPERIMENT.\n\
+\n\
+Known TESTs:\n",
+           (int) sysconf(_SC_NPROCESSORS_ONLN));
+    testrunner_base::print_names(stdout, 5);
+    printf("Or say TEST1,TEST2,... to run several tests in sequence\n\
+on the same tree.\n");
+    exit(0);
+}
+
+static void run_one_test(int trial, const char *treetype, const char *test,
+                         const int *collectorpipe, int nruns);
+enum { normtype_none, normtype_pertest, normtype_firsttest };
+static void print_gnuplot(FILE *f, const char * const *types_begin, const char * const *types_end, std::vector<String> &comparisons, int normalizetype);
+static void update_labnotebook(String notebook);
+
+#if HAVE_EXECINFO_H
+static const int abortable_signals[] = {
+    SIGSEGV, SIGBUS, SIGILL, SIGABRT, SIGFPE
+};
+
+static void abortable_signal_handler(int) {
+    // reset signals so if a signal recurs, we exit
+    for (const int* it = abortable_signals;
+         it != abortable_signals + arraysize(abortable_signals); ++it)
+        signal(*it, SIG_DFL);
+    // dump backtrace to standard error
+    void* return_addrs[50];
+    int n = backtrace(return_addrs, arraysize(return_addrs));
+    backtrace_symbols_fd(return_addrs, n, STDERR_FILENO);
+    // re-abort
+    abort();
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+    threadcounter_names[(int) tc_root_retry] = "root_retry";
+    threadcounter_names[(int) tc_internode_retry] = "internode_retry";
+    threadcounter_names[(int) tc_leaf_retry] = "leaf_retry";
+    threadcounter_names[(int) tc_leaf_walk] = "leaf_walk";
+    threadcounter_names[(int) tc_stable_internode_insert] = "stable_internode_insert";
+    threadcounter_names[(int) tc_stable_internode_split] = "stable_internode_split";
+    threadcounter_names[(int) tc_stable_leaf_insert] = "stable_leaf_insert";
+    threadcounter_names[(int) tc_stable_leaf_split] = "stable_leaf_split";
+    threadcounter_names[(int) tc_internode_lock] = "internode_lock_retry";
+    threadcounter_names[(int) tc_leaf_lock] = "leaf_lock_retry";
+
+    int ret, ntrials = 1, normtype = normtype_pertest, firstcore = -1, corestride = 1;
+    std::vector<const char *> tests, treetypes;
+    std::vector<String> comparisons;
+    const char *notebook = "notebook-mttest.json";
+    tcpthreads = udpthreads = sysconf(_SC_NPROCESSORS_ONLN);
+
+    Clp_Parser *clp = Clp_NewParser(argc, argv, (int) arraysize(options), options);
+    Clp_AddStringListType(clp, clp_val_normalize, 0,
+                          "none", (int) normtype_none,
+                          "pertest", (int) normtype_pertest,
+                          "test", (int) normtype_pertest,
+                          "firsttest", (int) normtype_firsttest,
+                          (const char *) 0);
+    Clp_AddType(clp, clp_val_suffixdouble, Clp_DisallowOptions, clp_parse_suffixdouble, 0);
+    int opt;
+    while ((opt = Clp_Next(clp)) != Clp_Done) {
+        switch (opt) {
+        case opt_pin:
+            pinthreads = !clp->negated;
+            break;
+        case opt_threads:
+            tcpthreads = udpthreads = clp->val.i;
+            break;
+        case opt_trials:
+            ntrials = clp->val.i;
+            break;
+        case opt_quiet:
+            quiet = !clp->negated;
+            break;
+        case opt_print:
+            print_table = !clp->negated;
+            break;
+        case opt_rscale_ncores:
+            rscale_ncores = clp->val.i;
+            break;
+        case opt_port:
+            port = clp->val.i;
+            break;
+        case opt_duration:
+            duration[0] = clp->val.d;
+            break;
+        case opt_lazy_timer:
+            lazy_timer = true;
+            break;
+        case opt_limit:
+            test_limit = uint64_t(clp->val.d);
+            break;
+        case opt_test:
+            tests.push_back(clp->vstr);
+            break;
+        case opt_test_name:
+            tests.push_back(clp->option->long_name + 5);
+            break;
+        case opt_normalize:
+            normtype = clp->negated ? normtype_none : clp->val.i;
+            break;
+        case opt_gid:
+            gid = clp->vstr;
+            break;
+        case opt_tree_stats:
+            tree_stats = true;
+            break;
+        case opt_stats:
+            json_stats = true;
+            break;
+        case opt_notebook:
+            if (clp->negated)
+                notebook = 0;
+            else if (clp->have_val)
+                notebook = clp->vstr;
+            else
+                notebook = "notebook-mttest.json";
+            break;
+        case opt_compare:
+            comparisons.push_back(clp->vstr);
+            break;
+        case opt_no_run:
+            ntrials = 0;
+            break;
+      case opt_cores:
+          if (firstcore >= 0 || cores.size() > 0) {
+              Clp_OptionError(clp, "%<%O%> already given");
+              exit(EXIT_FAILURE);
+          } else {
+              const char *plus = strchr(clp->vstr, '+');
+              Json ij = Json::parse(clp->vstr),
+                  aj = Json::parse(String("[") + String(clp->vstr) + String("]")),
+                  pj1 = Json::parse(plus ? String(clp->vstr, plus) : "x"),
+                  pj2 = Json::parse(plus ? String(plus + 1) : "x");
+              for (int i = 0; aj && i < aj.size(); ++i)
+                  if (!aj[i].is_int() || aj[i].to_i() < 0)
+                      aj = Json();
+              if (ij && ij.is_int() && ij.to_i() >= 0)
+                  firstcore = ij.to_i(), corestride = 1;
+              else if (pj1 && pj2 && pj1.is_int() && pj1.to_i() >= 0 && pj2.is_int())
+                  firstcore = pj1.to_i(), corestride = pj2.to_i();
+              else if (aj) {
+                  for (int i = 0; i < aj.size(); ++i)
+                      cores.push_back(aj[i].to_i());
+              } else {
+                  Clp_OptionError(clp, "bad %<%O%>, expected %<CORE1%>, %<CORE1+STRIDE%>, or %<CORE1,CORE2,...%>");
+                  exit(EXIT_FAILURE);
+              }
+          }
+          break;
+        case opt_help:
+            help();
+            break;
+        case Clp_NotOption:
+            // check for parameter setting
+            if (const char* eqchr = strchr(clp->vstr, '=')) {
+                Json& param = test_param[String(clp->vstr, eqchr)];
+                const char* end_vstr = clp->vstr + strlen(clp->vstr);
+                if (param.assign_parse(eqchr + 1, end_vstr))
+                    /* OK, param was valid JSON */;
+                else if (eqchr[1] != 0)
+                    param = String(eqchr + 1, end_vstr);
+                else
+                    param = Json();
+            } else {
+                // otherwise, tree or test
+                bool is_treetype = false;
+                for (int i = 0; i < (int) arraysize(test_thread_map) && !is_treetype; ++i)
+                    is_treetype = (strcmp(test_thread_map[i].treetype, clp->vstr) == 0);
+                (is_treetype ? treetypes.push_back(clp->vstr) : tests.push_back(clp->vstr));
+            }
+            break;
+        default:
+            fprintf(stderr, "Usage: mttest [-jN] TESTS...\n\
+Try 'mttest --help' for options.\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+    Clp_DeleteParser(clp);
+    if (firstcore < 0)
+        firstcore = cores.size() ? cores.back() + 1 : 0;
+    for (; (int) cores.size() < udpthreads; firstcore += corestride)
+        cores.push_back(firstcore);
+
+#if PMC_ENABLED
+    always_assert(pinthreads && "Using performance counter requires pinning threads to cores!");
+#endif
+#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
+    if (numa_available() != -1)
+        for (int i = 0; i <= numa_max_node(); i++) {
+            numa.push_back(mttest_numainfo());
+            numa.back().size = numa_node_size64(i, &numa.back().free);
+        }
+#endif
+#if HAVE_EXECINFO_H
+    for (const int* it = abortable_signals;
+         it != abortable_signals + arraysize(abortable_signals); ++it)
+        signal(*it, abortable_signal_handler);
+#endif
+
+    if (treetypes.empty())
+        treetypes.push_back("m");
+    if (tests.empty())
+        tests.push_back("rw1");
+
+    // arrange for a per-thread threadinfo pointer
+    ret = pthread_key_create(&threadinfo::key, 0);
+    always_assert(ret == 0);
+    pthread_mutex_init(&subtest_mutex, 0);
+    pthread_cond_init(&subtest_cond, 0);
+
+    // pipe for them to write back to us
+    int p[2];
+    ret = pipe(p);
+    always_assert(ret == 0);
+    test_output_file = fdopen(p[1], "w");
+
+    pthread_t collector;
+    ret = pthread_create(&collector, 0, stat_collector, (void *) (intptr_t) p[0]);
+    always_assert(ret == 0);
+    initial_timestamp = timestamp();
+
+    // run tests
+    int nruns = ntrials * (int) tests.size() * (int) treetypes.size();
+    std::vector<int> runlist(nruns, 0);
+    for (int i = 0; i < nruns; ++i)
+        runlist[i] = i;
+
+    for (int counter = 0; counter < nruns; ++counter) {
+        int x = random() % runlist.size();
+        int run = runlist[x];
+        runlist[x] = runlist.back();
+        runlist.pop_back();
+
+        int trial = run % ntrials;
+        run /= ntrials;
+        int t = run % tests.size();
+        run /= tests.size();
+        int tt = run;
+
+        fprintf(stderr, "%d/%u %s/%s%s", counter + 1, (int) (ntrials * tests.size() * treetypes.size()),
+                tests[t], treetypes[tt], quiet ? "      " : "\n");
+
+        run_one_test(trial, treetypes[tt], tests[t], p, nruns);
+        struct timeval delay;
+        delay.tv_sec = 0;
+        delay.tv_usec = 250000;
+        (void) select(0, 0, 0, 0, &delay);
+
+        if (quiet)
+            fprintf(stderr, "\r%60s\r", "");
+    }
+
+    fclose(test_output_file);
+    pthread_join(collector, 0);
+
+    // update lab notebook
+    if (notebook)
+        update_labnotebook(notebook);
+
+    // print Gnuplot
+    if (ntrials != 0)
+        comparisons.insert(comparisons.begin(), "");
+    if (!isatty(STDOUT_FILENO))
+        print_gnuplot(stdout, kvstats_name, kvstats_name + arraysize(kvstats_name),
+                      comparisons, normtype);
+
+    return 0;
+}
+
+static void run_one_test_body(int trial, const char *treetype, const char *test) {
+    threadinfo *main_ti = threadinfo::make(threadinfo::TI_MAIN, -1);
+    main_ti->run();
+    globalepoch = timestamp() >> 16;
+    for (int i = 0; i < (int) arraysize(test_thread_map); ++i)
+        if (strcmp(test_thread_map[i].treetype, treetype) == 0) {
+            current_test_name = test;
+            current_trial = trial;
+            test_thread_map[i].setup_func(main_ti, test_thread_initialize);
+            runtest(tcpthreads, test_thread_map[i].go_func);
+            if (tree_stats)
+                test_thread_map[i].setup_func(main_ti, test_thread_stats);
+            test_thread_map[i].setup_func(main_ti, test_thread_destroy);
+            break;
+        }
+}
+
+static void run_one_test(int trial, const char *treetype, const char *test,
+                         const int *collectorpipe, int nruns) {
+    if (nruns == 1)
+        run_one_test_body(trial, treetype, test);
+    else {
+        pid_t c = fork();
+        if (c == 0) {
+            close(collectorpipe[0]);
+            run_one_test_body(trial, treetype, test);
+            exit(0);
+        } else
+            while (waitpid(c, 0, 0) == -1 && errno == EINTR)
+                /* loop */;
+    }
+}
+
+static double level(const std::vector<double> &v, double frac) {
+    frac *= v.size() - 1;
+    int base = (int) frac;
+    if (base == frac)
+        return v[base];
+    else
+        return v[base] * (1 - (frac - base)) + v[base + 1] * (frac - base);
+}
+
+static String experiment_test_table_trial(const String &key) {
+    const char *l = key.begin(), *r = key.end();
+    if (l + 2 < r && l[0] == 'x' && isdigit((unsigned char) l[1])) {
+        for (const char *s = l; s != r; ++s)
+            if (*s == '/') {
+                l = s + 1;
+                break;
+            }
+    }
+    return key.substring(l, r);
+}
+
+static String experiment_run_test_table(const String &key) {
+    const char *l = key.begin(), *r = key.end();
+    for (const char *s = r; s != l; --s)
+        if (s[-1] == '/') {
+            r = s - 1;
+            break;
+        } else if (!isdigit((unsigned char) s[-1]))
+            break;
+    return key.substring(l, r);
+}
+
+static String experiment_test_table(const String &key) {
+    return experiment_run_test_table(experiment_test_table_trial(key));
+}
+
+namespace {
+struct gnuplot_info {
+    static constexpr double trialdelta = 0.015, treetypedelta = 0.04,
+        testdelta = 0.08, typedelta = 0.2;
+    double pos;
+    double nextdelta;
+    double normalization;
+    String last_test;
+    int normalizetype;
+
+    std::vector<lcdf::StringAccum> candlesticks;
+    std::vector<lcdf::StringAccum> medians;
+    lcdf::StringAccum xtics;
+
+    gnuplot_info(int nt)
+        : pos(1 - trialdelta), nextdelta(trialdelta), normalization(-1),
+          normalizetype(nt) {
+    }
+    void one(const String &xname, int ti, const String &datatype_name);
+    void print(FILE *f, const char * const *types_begin);
+};
+constexpr double gnuplot_info::trialdelta, gnuplot_info::treetypedelta, gnuplot_info::testdelta, gnuplot_info::typedelta;
+
+void gnuplot_info::one(const String &xname, int ti, const String &datatype_name)
+{
+    String current_test = experiment_test_table(xname);
+    if (current_test != last_test) {
+        last_test = current_test;
+        if (normalizetype == normtype_pertest)
+            normalization = -1;
+        if (nextdelta == treetypedelta)
+            nextdelta = testdelta;
+    }
+    double beginpos = pos, firstpos = pos + nextdelta;
+
+    std::vector<int> trials;
+    for (Json::object_iterator it = experiment_stats.obegin();
+         it != experiment_stats.oend(); ++it) {
+        String key = it.key();
+        if (experiment_run_test_table(key) == xname)
+            trials.push_back(strtol(key.c_str() + xname.length() + 1, 0, 0));
+    }
+    std::sort(trials.begin(), trials.end());
+
+    for (std::vector<int>::iterator tit = trials.begin();
+         tit != trials.end(); ++tit) {
+        Json &this_trial = experiment_stats[xname + "/" + String(*tit)];
+        std::vector<double> values;
+        for (int jn = 0; jn < this_trial.size(); ++jn)
+            if (this_trial[jn].get(datatype_name).is_number())
+                values.push_back(this_trial[jn].get(datatype_name).to_d());
+        if (values.size()) {
+            pos += nextdelta;
+            std::sort(values.begin(), values.end());
+            if (normalization < 0)
+                normalization = normalizetype == normtype_none ? 1 : level(values, 0.5);
+            if (int(candlesticks.size()) <= ti) {
+                candlesticks.resize(ti + 1);
+                medians.resize(ti + 1);
+            }
+            candlesticks[ti] << pos << " " << level(values, 0)
+                             << " " << level(values, 0.25)
+                             << " " << level(values, 0.75)
+                             << " " << level(values, 1)
+                             << " " << normalization << "\n";
+            medians[ti] << pos << " " << level(values, 0.5) << " " << normalization << "\n";
+            nextdelta = trialdelta;
+        }
+    }
+
+    if (pos > beginpos) {
+        double middle = (firstpos + pos) / 2;
+        xtics << (xtics ? ", " : "") << "\"" << xname << "\" " << middle;
+        nextdelta = treetypedelta;
+    }
+}
+
+void gnuplot_info::print(FILE *f, const char * const *types_begin) {
+    std::vector<int> linetypes(medians.size(), 0);
+    int next_linetype = 1;
+    for (int i = 0; i < int(medians.size()); ++i)
+        if (medians[i])
+            linetypes[i] = next_linetype++;
+    struct utsname name;
+    fprintf(f, "set title \"%s (%d cores)\"\n",
+            (uname(&name) == 0 ? name.nodename : "unknown"),
+            udpthreads);
+    fprintf(f, "set terminal png\n");
+    fprintf(f, "set xrange [%g:%g]\n", 1 - treetypedelta, pos + treetypedelta);
+    fprintf(f, "set xtics rotate by 45 right (%s) font \"Verdana,9\"\n", xtics.c_str());
+    fprintf(f, "set key top left Left reverse\n");
+    if (normalizetype == normtype_none)
+        fprintf(f, "set ylabel \"count\"\n");
+    else if (normalizetype == normtype_pertest)
+        fprintf(f, "set ylabel \"count, normalized per test\"\n");
+    else
+        fprintf(f, "set ylabel \"normalized count (1=%f)\"\n", normalization);
+    const char *sep = "plot ";
+    for (int i = 0; i < int(medians.size()); ++i)
+        if (medians[i]) {
+            fprintf(f, "%s '-' using 1:($3/$6):($2/$6):($5/$6):($4/$6) with candlesticks lt %d title '%s', \\\n",
+                    sep, linetypes[i], types_begin[i]);
+            fprintf(f, " '-' using 1:($2/$3):($2/$3):($2/$3):($2/$3) with candlesticks lt %d notitle", linetypes[i]);
+            sep = ", \\\n";
+        }
+    fprintf(f, "\n");
+    for (int i = 0; i < int(medians.size()); ++i)
+        if (medians[i]) {
+            fwrite(candlesticks[i].begin(), 1, candlesticks[i].length(), f);
+            fprintf(f, "e\n");
+            fwrite(medians[i].begin(), 1, medians[i].length(), f);
+            fprintf(f, "e\n");
+        }
+}
+
+}
+
+static void print_gnuplot(FILE *f, const char * const *types_begin, const char * const *types_end,
+                          std::vector<String> &comparisons, int normalizetype) {
+    for (std::vector<String>::iterator cit = comparisons.begin();
+         cit != comparisons.end(); ++cit) {
+        if (!*cit)
+            *cit = "[^x]*";
+        else if (cit->length() >= 2 && (*cit)[0] == 'x' && isdigit((unsigned char) (*cit)[1]))
+            *cit += String(cit->find_left('/') < 0 ? "/*" : "*");
+        else
+            *cit = String("x*") + *cit + String("*");
+    }
+
+    std::vector<String> all_versions, all_experiments;
+    for (Json::object_iterator it = experiment_stats.obegin();
+         it != experiment_stats.oend(); ++it)
+        for (std::vector<String>::const_iterator cit = comparisons.begin();
+             cit != comparisons.end(); ++cit)
+            if (it.key().glob_match(*cit)) {
+                all_experiments.push_back(experiment_run_test_table(it.key()));
+                all_versions.push_back(experiment_test_table(it.key()));
+                break;
+            }
+    std::sort(all_experiments.begin(), all_experiments.end());
+    all_experiments.erase(std::unique(all_experiments.begin(), all_experiments.end()),
+                          all_experiments.end());
+    std::sort(all_versions.begin(), all_versions.end());
+    all_versions.erase(std::unique(all_versions.begin(), all_versions.end()),
+                       all_versions.end());
+
+    int ntypes = (int) (types_end - types_begin);
+    gnuplot_info gpinfo(normalizetype);
+
+    for (int ti = 0; ti < ntypes; ++ti) {
+        double typepos = gpinfo.pos;
+        for (std::vector<String>::iterator vit = all_versions.begin();
+             vit != all_versions.end(); ++vit) {
+            for (std::vector<String>::iterator xit = all_experiments.begin();
+                 xit != all_experiments.end(); ++xit)
+                if (experiment_test_table(*xit) == *vit)
+                    gpinfo.one(*xit, ti, types_begin[ti]);
+        }
+        if (gpinfo.pos > typepos)
+            gpinfo.nextdelta = gpinfo.typedelta;
+        gpinfo.last_test = "";
+    }
+
+    if (gpinfo.xtics)
+        gpinfo.print(f, types_begin);
+}
+
+static String
+read_file(FILE *f, const char *name)
+{
+    lcdf::StringAccum sa;
+    while (1) {
+        size_t x = fread(sa.reserve(4096), 1, 4096, f);
+        if (x != 0)
+            sa.adjust_length(x);
+        else if (ferror(f)) {
+            fprintf(stderr, "%s: %s\n", name, strerror(errno));
+            return String::make_stable("???", 3);
+        } else
+            return sa.take_string();
+    }
+}
+
+static void
+update_labnotebook(String notebook)
+{
+    FILE *f = (notebook == "-" ? stdin : fopen(notebook.c_str(), "r"));
+    String previous_text = (f ? read_file(f, notebook.c_str()) : String());
+    if (previous_text.out_of_memory())
+        return;
+    if (f && f != stdin)
+        fclose(f);
+
+    Json nb = Json::parse(previous_text);
+    if (previous_text && (!nb.is_object() || !nb["experiments"])) {
+        fprintf(stderr, "%s: unexpected contents, not writing new data\n", notebook.c_str());
+        return;
+    }
+
+    if (!nb)
+        nb = Json::make_object();
+    if (!nb.get("experiments"))
+        nb.set("experiments", Json::make_object());
+    if (!nb.get("data"))
+        nb.set("data", Json::make_object());
+
+    Json old_data = nb["data"];
+    if (!experiment_stats) {
+        experiment_stats = old_data;
+        return;
+    }
+
+    Json xjson;
+
+    FILE *git_info_p = popen("git rev-parse HEAD | tr -d '\n'; git --no-pager diff --exit-code --shortstat HEAD >/dev/null 2>&1 || echo M", "r");
+    String git_info = read_file(git_info_p, "<git output>");
+    pclose(git_info_p);
+    if (git_info)
+        xjson.set("git-revision", git_info.trim());
+
+    time_t now = time(0);
+    xjson.set("time", String(ctime(&now)).trim());
+    if (gid)
+        xjson.set("gid", String(gid));
+
+    struct utsname name;
+    if (uname(&name) == 0)
+        xjson.set("machine", name.nodename);
+
+    xjson.set("cores", udpthreads);
+
+    Json &runs = xjson.get_insert("runs");
+    String xname = "x" + String(nb["experiments"].size());
+    for (Json::const_iterator it = experiment_stats.begin();
+         it != experiment_stats.end(); ++it) {
+        String xkey = xname + "/" + it.key();
+        runs.push_back(xkey);
+        nb["data"][xkey] = it.value();
+    }
+    xjson.set("runs", runs);
+
+    nb["experiments"][xname] = xjson;
+
+    String new_text = nb.unparse(Json::indent_depth(4).tab_width(2).newline_terminator(true));
+    f = (notebook == "-" ? stdout : fopen((notebook + "~").c_str(), "w"));
+    if (!f) {
+        fprintf(stderr, "%s~: %s\n", notebook.c_str(), strerror(errno));
+        return;
+    }
+    size_t written = fwrite(new_text.data(), 1, new_text.length(), f);
+    if (written != size_t(new_text.length())) {
+        fprintf(stderr, "%s~: %s\n", notebook.c_str(), strerror(errno));
+        fclose(f);
+        return;
+    }
+    if (f != stdout) {
+        fclose(f);
+        if (rename((notebook + "~").c_str(), notebook.c_str()) != 0)
+            fprintf(stderr, "%s: %s\n", notebook.c_str(), strerror(errno));
+    }
+
+    fprintf(stderr, "EXPERIMENT %s\n", xname.c_str());
+    experiment_stats.merge(old_data);
+}
diff --git a/silo/masstree/nodeversion.hh b/silo/masstree/nodeversion.hh
new file mode 100644 (file)
index 0000000..92cbcf0
--- /dev/null
@@ -0,0 +1,343 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef MASSTREE_NODEVERSION_HH
+#define MASSTREE_NODEVERSION_HH
+#include "compiler.hh"
+
+template <typename P>
+class basic_nodeversion {
+  public:
+    typedef P traits_type;
+    typedef typename P::value_type value_type;
+
+    basic_nodeversion() {
+    }
+    explicit basic_nodeversion(bool isleaf) {
+        v_ = isleaf ? (value_type) P::isleaf_bit : 0;
+    }
+
+    bool isleaf() const {
+        return v_ & P::isleaf_bit;
+    }
+
+    basic_nodeversion<P> stable() const {
+        return stable(relax_fence_function());
+    }
+    template <typename SF>
+    basic_nodeversion<P> stable(SF spin_function) const {
+        value_type x = v_;
+        while (x & P::dirty_mask) {
+            spin_function();
+            x = v_;
+        }
+        acquire_fence();
+        return x;
+    }
+    template <typename SF>
+    basic_nodeversion<P> stable_annotated(SF spin_function) const {
+        value_type x = v_;
+        while (x & P::dirty_mask) {
+            spin_function(basic_nodeversion<P>(x));
+            x = v_;
+        }
+        acquire_fence();
+        return x;
+    }
+
+    bool locked() const {
+        return v_ & P::lock_bit;
+    }
+    bool inserting() const {
+        return v_ & P::inserting_bit;
+    }
+    bool splitting() const {
+        return v_ & P::splitting_bit;
+    }
+    bool deleted() const {
+        return v_ & P::deleted_bit;
+    }
+    bool has_changed(basic_nodeversion<P> x) const {
+        fence();
+        return (x.v_ ^ v_) > P::lock_bit;
+    }
+    bool has_split() const {
+        return !(v_ & P::root_bit);
+    }
+    bool has_split(basic_nodeversion<P> x) const {
+        fence();
+        return (x.v_ ^ v_) >= P::vsplit_lowbit;
+    }
+    bool simple_has_split(basic_nodeversion<P> x) const {
+        return (x.v_ ^ v_) >= P::vsplit_lowbit;
+    }
+
+    basic_nodeversion<P> lock() {
+        return lock(*this);
+    }
+    basic_nodeversion<P> lock(basic_nodeversion<P> expected) {
+        return lock(expected, relax_fence_function());
+    }
+    template <typename SF>
+    basic_nodeversion<P> lock(basic_nodeversion<P> expected, SF spin_function) {
+        while (1) {
+            if (!(expected.v_ & P::lock_bit)
+                && bool_cmpxchg(&v_, expected.v_,
+                                expected.v_ | P::lock_bit))
+                break;
+            spin_function();
+            expected.v_ = v_;
+        }
+        masstree_invariant(!(expected.v_ & P::dirty_mask));
+        expected.v_ |= P::lock_bit;
+        acquire_fence();
+        masstree_invariant(expected.v_ == v_);
+        return expected;
+    }
+
+    void unlock() {
+        unlock(*this);
+    }
+    void unlock(basic_nodeversion<P> x) {
+        masstree_invariant((fence(), x.v_ == v_));
+        masstree_invariant(x.v_ & P::lock_bit);
+        if (x.v_ & P::splitting_bit)
+            x.v_ = (x.v_ + P::vsplit_lowbit) & P::split_unlock_mask;
+        else
+            x.v_ = (x.v_ + ((x.v_ & P::inserting_bit) << 2)) & P::unlock_mask;
+        release_fence();
+        v_ = x.v_;
+    }
+
+    void mark_insert() {
+        masstree_invariant(locked());
+        v_ |= P::inserting_bit;
+        acquire_fence();
+    }
+    basic_nodeversion<P> mark_insert(basic_nodeversion<P> current_version) {
+        masstree_invariant((fence(), v_ == current_version.v_));
+        masstree_invariant(current_version.v_ & P::lock_bit);
+        v_ = (current_version.v_ |= P::inserting_bit);
+        acquire_fence();
+        return current_version;
+    }
+    void mark_split() {
+        masstree_invariant(locked());
+        v_ |= P::splitting_bit;
+        acquire_fence();
+    }
+    void mark_change(bool is_split) {
+        masstree_invariant(locked());
+        v_ |= (is_split + 1) << P::inserting_shift;
+        acquire_fence();
+    }
+    basic_nodeversion<P> mark_deleted() {
+        masstree_invariant(locked());
+        v_ |= P::deleted_bit | P::splitting_bit;
+        acquire_fence();
+        return *this;
+    }
+    void mark_deleted_tree() {
+        masstree_invariant(locked() && !has_split());
+        v_ |= P::deleted_bit;
+        acquire_fence();
+    }
+    void mark_root() {
+        v_ |= P::root_bit;
+        acquire_fence();
+    }
+    void mark_nonroot() {
+        v_ &= ~P::root_bit;
+        acquire_fence();
+    }
+
+    void assign_version(basic_nodeversion<P> x) {
+        v_ = x.v_;
+    }
+
+    value_type version_value() const {
+        return v_;
+    }
+    value_type unlocked_version_value() const {
+        return v_ & P::unlock_mask;
+    }
+
+  private:
+    value_type v_;
+
+    basic_nodeversion(value_type v)
+        : v_(v) {
+    }
+};
+
+
+template <typename P>
+class basic_singlethreaded_nodeversion {
+  public:
+    typedef P traits_type;
+    typedef typename P::value_type value_type;
+
+    basic_singlethreaded_nodeversion() {
+    }
+    explicit basic_singlethreaded_nodeversion(bool isleaf) {
+        v_ = isleaf ? (value_type) P::isleaf_bit : 0;
+    }
+
+    bool isleaf() const {
+        return v_ & P::isleaf_bit;
+    }
+
+    basic_singlethreaded_nodeversion<P> stable() const {
+        return *this;
+    }
+    template <typename SF>
+    basic_singlethreaded_nodeversion<P> stable(SF) const {
+        return *this;
+    }
+    template <typename SF>
+    basic_singlethreaded_nodeversion<P> stable_annotated(SF) const {
+        return *this;
+    }
+
+    bool locked() const {
+        return false;
+    }
+    bool inserting() const {
+        return false;
+    }
+    bool splitting() const {
+        return false;
+    }
+    bool deleted() const {
+        return false;
+    }
+    bool has_changed(basic_singlethreaded_nodeversion<P>) const {
+        return false;
+    }
+    bool has_split() const {
+        return !(v_ & P::root_bit);
+    }
+    bool has_split(basic_singlethreaded_nodeversion<P>) const {
+        return false;
+    }
+    bool simple_has_split(basic_singlethreaded_nodeversion<P>) const {
+        return false;
+    }
+
+    basic_singlethreaded_nodeversion<P> lock() {
+        return *this;
+    }
+    basic_singlethreaded_nodeversion<P> lock(basic_singlethreaded_nodeversion<P>) {
+        return *this;
+    }
+    template <typename SF>
+    basic_singlethreaded_nodeversion<P> lock(basic_singlethreaded_nodeversion<P>, SF) {
+        return *this;
+    }
+
+    void unlock() {
+    }
+    void unlock(basic_singlethreaded_nodeversion<P>) {
+    }
+
+    void mark_insert() {
+    }
+    basic_singlethreaded_nodeversion<P> mark_insert(basic_singlethreaded_nodeversion<P>) {
+        return *this;
+    }
+    void mark_split() {
+        v_ &= ~P::root_bit;
+    }
+    void mark_change(bool is_split) {
+        if (is_split)
+            mark_split();
+    }
+    basic_singlethreaded_nodeversion<P> mark_deleted() {
+        return *this;
+    }
+    void mark_deleted_tree() {
+        v_ |= P::deleted_bit;
+    }
+    void mark_root() {
+        v_ |= P::root_bit;
+    }
+    void mark_nonroot() {
+        v_ &= ~P::root_bit;
+    }
+
+    void assign_version(basic_singlethreaded_nodeversion<P> x) {
+        v_ = x.v_;
+    }
+
+    value_type version_value() const {
+        return v_;
+    }
+    value_type unlocked_version_value() const {
+        return v_;
+    }
+
+  private:
+    value_type v_;
+};
+
+
+struct nodeversion32_parameters {
+    enum {
+        lock_bit = (1U << 0),
+        inserting_shift = 1,
+        inserting_bit = (1U << 1),
+        splitting_bit = (1U << 2),
+        dirty_mask = inserting_bit | splitting_bit,
+        vinsert_lowbit = (1U << 3), // == inserting_bit << 2
+        vsplit_lowbit = (1U << 9),
+        unused1_bit = (1U << 28),
+        deleted_bit = (1U << 29),
+        root_bit = (1U << 30),
+        isleaf_bit = (1U << 31),
+        split_unlock_mask = ~(root_bit | unused1_bit | (vsplit_lowbit - 1)),
+        unlock_mask = ~(unused1_bit | (vinsert_lowbit - 1)),
+        top_stable_bits = 4
+    };
+
+    typedef uint32_t value_type;
+};
+
+
+struct nodeversion64_parameters {
+    enum {
+        lock_bit = (1ULL << 8),
+        inserting_shift = 9,
+        inserting_bit = (1ULL << 9),
+        splitting_bit = (1ULL << 10),
+        dirty_mask = inserting_bit | splitting_bit,
+        vinsert_lowbit = (1ULL << 11), // == inserting_bit << 2
+        vsplit_lowbit = (1ULL << 27),
+        unused1_bit = (1ULL << 60),
+        deleted_bit = (1ULL << 61),
+        root_bit = (1ULL << 62),
+        isleaf_bit = (1ULL << 63),
+        split_unlock_mask = ~(root_bit | unused1_bit | (vsplit_lowbit - 1)),
+        unlock_mask = ~(unused1_bit | (vinsert_lowbit - 1)),
+        top_stable_bits = 4
+    };
+
+    typedef uint64_t value_type;
+};
+
+
+typedef basic_nodeversion<nodeversion32_parameters> nodeversion;
+typedef basic_singlethreaded_nodeversion<nodeversion32_parameters> singlethreaded_nodeversion;
+
+#endif
diff --git a/silo/masstree/perfstat.cc b/silo/masstree/perfstat.cc
new file mode 100644 (file)
index 0000000..85e93a4
--- /dev/null
@@ -0,0 +1,216 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "perfstat.hh"
+#include "compiler.hh"
+#include "kvstats.hh"
+#if HAVE_NUMA_H
+#include <numa.h>
+#endif
+
+enum { MaxCores = 48 };   // Maximum number of cores kvdb statistics support
+enum { MaxNumaNode = 8 }; // Maximum number of Numa node kvdb statistics support
+enum { CoresPerChip = MaxCores / MaxNumaNode };
+
+namespace Perf {
+
+#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
+static struct {
+  long long free;
+  long long size;
+} numa[MaxNumaNode];
+#endif
+
+void
+stat::initmain(bool pinthreads) {
+    (void) pinthreads;
+#if PMC_ENABLED
+    always_assert(pinthreads && "Using performance counter requires pinning threads to cores!");
+#endif
+#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
+    if (numa_available() != -1) {
+        always_assert(numa_max_node() <= MaxNumaNode);
+        for (int i = 0; i <= numa_max_node(); i++)
+            numa[i].size = numa_node_size64(i, &numa[i].free);
+    }
+#endif
+}
+
+template <typename T>
+kvstats
+sum_all_cores(const stat **s, int n, const int offset) {
+    kvstats sum;
+    for (int i = 0; i < n; i++) {
+        if (!s[i])
+            continue;
+        T v = *reinterpret_cast<const T *>(reinterpret_cast<const char *>(s[i]) + offset);
+        sum.add(v);
+    }
+    return sum;
+}
+
+template <typename T>
+kvstats
+sum_one_chip(const stat **s, int n, const int offset, const int chipidx) {
+    kvstats sum;
+    for (int i = 0; i < n; i++) {
+        if (!s[i] || s[i]->cid / (MaxCores / MaxNumaNode) != chipidx)
+            continue;
+        T v = *reinterpret_cast<const T *>(reinterpret_cast<const char *>(s[i]) + offset);
+        sum.add(v);
+    }
+    return sum;
+}
+
+template <typename T>
+kvstats
+sum_all_per_chip(const stat **s, int n, const int offset) {
+    kvstats per_chip[MaxNumaNode];
+    for (int i  = 0; i < n; i++) {
+        if (!s[i])
+            continue;
+        T v = *reinterpret_cast<const T *>(reinterpret_cast<const char *>(s[i]) + offset);
+        per_chip[i / CoresPerChip].add(v);
+    }
+    kvstats sum;
+    for (int i = 0; i < MaxNumaNode; i++)
+        if (per_chip[i].count)
+            sum.add(per_chip[i].avg());
+    return sum;
+}
+
+void
+stat::print(const stat **s, int n) {
+    (void)n;
+    (void)s;
+#define sum_all_cores_of(field) \
+    sum_all_cores<typeof(s[0]->field)>(s, n, offsetof(Perf::stat, field))
+#define sum_one_chip_of(field, c) \
+    sum_one_chip<typeof(s[0]->field)>(s, n, offsetof(Perf::stat, field), c)
+#define sum_all_per_chip_of(field) \
+    sum_all_per_chip<typeof(s[0]->field)>(s, n, offsetof(Perf::stat, field))
+
+#define sum_all_cores_of_array(field, oa) \
+    sum_all_cores<typeof(s[0]->field[0])>(s, n, offsetof(Perf::stat, field) + \
+                                          sizeof(s[0]->field[0]) * oa)
+#define sum_one_chip_of_array(field, oa, c) \
+    sum_one_chip<typeof(s[0]->field[0])>(s, n, offsetof(Perf::stat, field) + \
+                                         sizeof(s[0]->field[0]) * oa, c)
+#define sum_all_per_chip_of_array(field, oa) \
+    sum_all_per_chip<typeof(s[0]->field[0])>(s, n, offsetof(Perf::stat, field) + \
+                                             sizeof(s[0]->field[0]) * oa)
+
+#if GETSTATS && 0
+    for (int i = 0; i < n; i++)
+        if (s[i]->ngets < 1000) {
+            s[i] = NULL;
+            continue;
+        }
+    kvstats ngets = sum_all_cores_of(ngets);
+    kvstats ntsc = sum_all_cores_of(ntsc);
+    kvstats np = sum_all_cores_of(nprobe);
+    if (np.sum >= 1)
+        fprintf(stderr, "Total probe %.0f, probe/get %.2f\n", np.sum, np.sum / ngets.sum);
+#if PMC_ENABLED
+    fprintf(stderr, "(Inaccurate because PMC is Enabled!)");
+#endif
+    fprintf(stderr, "Cycles/get (between mark_get_begin and mark_get_end): %.0f\n",
+            ntsc.sum / ngets.sum);
+#if PMC_ENABLED
+    for (int i = 0; i < n; i++) {
+        if (!s[i])
+            continue;
+        fprintf(stderr, "Core %d:\n", i);
+        for (int pi = 0; pi < 4; pi++) {
+            fprintf(stderr, "\tpmc[%d]: %016" PRIx64 "->%016" PRIx64 "\n",
+                    pi, s[i]->pmc_firstget[pi], s[i]->pmc_start[pi]);
+            always_assert(s[i]->pmc_start[pi] >= s[i]->pmc_firstget[pi]);
+            always_assert(s[i]->t1_lastget >= s[i]->t0_firstget);
+        }
+    }
+    // Compute the start and end time of get phase
+    kvstats getstart = sum_all_cores_of(t0_firstget);
+    kvstats getend = sum_all_cores_of(t1_lastget);
+    getstart.print_report("time of first get");
+    getend.print_report("time of last get");
+
+    // Compute per-chip pmc during the whole get phase
+    double pcpmc_phase[MaxNumaNode][4];
+    for (int i = 0; i < MaxNumaNode; i++)
+        for (int pi = 0; pi < 4; pi++)
+            pcpmc_phase[i][pi] = sum_one_chip_of_array(pmc_start, pi, i).avg() - 
+                                 sum_one_chip_of_array(pmc_firstget, pi, i).avg();
+
+    // Compute cputime and realtime during get phase
+    kvstats t_firstget = sum_all_cores_of(t0_firstget);
+    kvstats t_lastget = sum_all_cores_of(t1_lastget);
+    double realtime = t_lastget.avg() - t_firstget.avg();
+
+    for (int pi = 0; pi < 4; pi++) {
+        fprintf(stderr, "DRAM access to node (pmc %d)\n", pi);
+        double sum = 0;
+        for (int i = 0; i < MaxNumaNode; i++) {
+            fprintf(stderr, "\tFrom chip %2d: %8.1f GB/s\n", i,
+                    pcpmc_phase[i][pi] * 64 / (realtime * (1 << 30)));
+            sum += pcpmc_phase[i][pi];
+        }
+        fprintf(stderr, "\tSum: %8.1f GB/s\n", 
+                sum * 64 / (realtime * (1 << 30)));
+    }
+    // Print per-get pmc_lookup
+    fprintf(stderr, "Per get statistics (counted between mark_get_begin and mark_get_end):\n");
+    for (int pi = 0; (ngets.sum > 0) && pi < 4; pi ++) {
+        kvstats pmc_lookup = sum_all_cores_of_array(pmc_lookup, pi);
+        kvstats pcpmc_lookup = sum_all_per_chip_of_array(pmc_lookup, pi);
+        fprintf(stderr, "\tpmc%d/get: %6.1f, per_chip_pmc%d/get: %6.1f\n",
+                pi, (double) pmc_lookup.sum / ngets.sum, pi, 
+               (double) pcpmc_lookup.sum / ngets.sum);
+    }
+#endif
+#endif
+
+#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA && 0
+    // collect tree memory
+    kvstats tree_mem = sum_all_cores_of(tree_mem);
+    kvstats tree_keys = sum_all_cores_of(tree_keys);
+    fprintf(stderr, "Memory statistics\n");
+    fprintf(stderr, "\tAllocated per key: %.0f bytes, %.0f\n", tree_mem.sum / tree_keys.sum, tree_keys.sum);
+    if (numa_available() != -1) {
+        unsigned long total_alloc = 0;
+        for (int i = 0; i <= numa_max_node(); i++) {
+            kvstats chip = sum_one_chip_of(tree_mem, i);
+            long long nowfree;
+            long long size = numa_node_size64(i, &nowfree);
+            total_alloc += numa[i].free - nowfree;
+            fprintf(stderr, "\tNode %d (MB): size %6lld, allocated = %6lld - "
+                    "%6lld = %6lld, tree_mem %6.0f\n",
+                    i, size >> 20, numa[i].free >> 20, nowfree >> 20,
+                    (numa[i].free - nowfree) / (1 << 20), 
+                    chip.sum / (1 << 20));
+        }
+        fprintf(stderr, "Total allocated memory %ld MB\n", total_alloc >> 20);
+    }
+#endif
+
+#if GCSTATS
+    // collect memory used by epoch based garbage collector
+    kvstats gc_nfree = sum_all_cores_of(gc_nfree);
+    kvstats gc_nalloc = sum_all_cores_of(gc_nalloc);
+    fprintf(stderr, "reuse per gc slot: %.0f, freed: %.0f, allocated: %.0f\n",
+            gc_nfree.sum / gc_nalloc.sum, gc_nfree.sum, gc_nalloc.sum);
+#endif
+}
+
+}
diff --git a/silo/masstree/perfstat.hh b/silo/masstree/perfstat.hh
new file mode 100644 (file)
index 0000000..fe37ea5
--- /dev/null
@@ -0,0 +1,38 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef PERF_STAT_HH
+#define PERF_STAT_HH 1
+#include "compiler.hh"
+#include "misc.hh"
+#include <stdlib.h>
+#include <inttypes.h>
+
+namespace Perf {
+struct stat {
+    /** @brief An initialization call from main function
+     */
+    static void initmain(bool pinthreads);
+#if GCSTATS
+    int gc_nfree;
+#endif
+    void initialize(int cid) {
+        this->cid = cid;
+    }
+    static void print(const stat **s, int n);
+    int cid;    // core index
+};
+}
+#endif
diff --git a/silo/masstree/query_masstree.cc b/silo/masstree/query_masstree.cc
new file mode 100644 (file)
index 0000000..064dde8
--- /dev/null
@@ -0,0 +1,424 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "masstree.hh"
+#include "masstree_key.hh"
+#include "masstree_struct.hh"
+#include "masstree_tcursor.hh"
+#include "masstree_get.hh"
+#include "masstree_insert.hh"
+#include "masstree_split.hh"
+#include "masstree_remove.hh"
+#include "masstree_scan.hh"
+#include "masstree_print.hh"
+#include "query_masstree.hh"
+#include "string_slice.hh"
+#include "kpermuter.hh"
+#include "ksearch.hh"
+#include "stringbag.hh"
+#include "json.hh"
+#include "kvrow.hh"
+
+namespace Masstree {
+
+static uint64_t heightcounts[300], fillcounts[100];
+
+template <typename P>
+static void treestats1(node_base<P>* n, unsigned height) {
+    if (!n)
+        return;
+    if (n->isleaf()) {
+        assert(height < arraysize(heightcounts));
+        if (n->deleted())
+            return;
+        leaf<P> *lf = (leaf<P> *)n;
+        typename leaf<P>::permuter_type perm = lf->permutation_;
+        for (int idx = 0; idx < perm.size(); ++idx) {
+            int p = perm[idx];
+            typename leaf<P>::leafvalue_type lv = lf->lv_[p];
+            if (!lv || !lf->is_layer(p))
+                heightcounts[height] ++;
+            else {
+                node_base<P> *in = lv.layer()->unsplit_ancestor();
+                treestats1(in, height + 1);
+            }
+        }
+    } else {
+        internode<P> *in = (internode<P> *) n;
+        for (int i = 0; i <= n->size(); ++i)
+            if (in->child_[i])
+                treestats1(in->child_[i], height + 1);
+    }
+    assert((size_t) n->size() < arraysize(fillcounts));
+    fillcounts[n->size()] += 1;
+}
+
+template <typename P>
+void query_table<P>::stats(FILE* f) {
+    memset(heightcounts, 0, sizeof(heightcounts));
+    memset(fillcounts, 0, sizeof(fillcounts));
+    treestats1(table_.root(), 0);
+    fprintf(f, "  heights:");
+    for (unsigned i = 0; i < arraysize(heightcounts); ++i)
+        if (heightcounts[i])
+            fprintf(f, "  %d=%" PRIu64, i, heightcounts[i]);
+    fprintf(f, "\n  node counts:");
+    for (unsigned i = 0; i < arraysize(fillcounts); ++i)
+        if (fillcounts[i])
+            fprintf(f, "  %d=%" PRIu64, i, fillcounts[i]);
+    fprintf(f, "\n");
+}
+
+template <typename P>
+static void json_stats1(node_base<P>* n, lcdf::Json& j, int layer, int depth,
+                        threadinfo& ti)
+{
+    if (!n)
+        return;
+    else if (n->isleaf()) {
+        leaf<P>* lf = static_cast<leaf<P>*>(n);
+        // track number of leaves by depth and size
+        j[&"l1_node_by_depth"[!layer * 3]][depth] += 1;
+        j[&"l1_leaf_by_depth"[!layer * 3]][depth] += 1;
+        j[&"l1_leaf_by_size"[!layer * 3]][lf->size()] += 1;
+
+        // per-key information
+        typename leaf<P>::permuter_type perm(lf->permutation_);
+        int n = 0, nksuf = 0;
+        size_t active_ksuf_len = 0;
+        for (int i = 0; i < perm.size(); ++i)
+            if (lf->is_layer(perm[i])) {
+                lcdf::Json x = j["l1_size"];
+                j["l1_size"] = 0;
+                json_stats1(lf->lv_[perm[i]].layer(), j, layer + 1, 0, ti);
+                j["l1_size_sum"] += j["l1_size"].to_i();
+                j["l1_size"] = x;
+                j["l1_count"] += 1;
+            } else {
+                ++n;
+                int l = sizeof(typename P::ikey_type) * layer
+                    + lf->keylenx_[perm[i]];
+                if (lf->has_ksuf(perm[i])) {
+                    size_t ksuf_len = lf->ksuf(perm[i]).len;
+                    l += ksuf_len - 1;
+                    active_ksuf_len += ksuf_len;
+                    ++nksuf;
+                }
+                j["key_by_length"][l] += 1;
+            }
+        j["size"] += n;
+        j["l1_size"] += n;
+        j["key_by_layer"][layer] += n;
+
+        // key suffix information
+        if (lf->allocated_size() != lf->min_allocated_size()
+            && lf->ksuf_external()) {
+            j["overridden_ksuf"] += 1;
+            j["overridden_ksuf_capacity"] += lf->allocated_size() - lf->min_allocated_size();
+        }
+        if (lf->ksuf_capacity()) {
+            j["ksuf"] += 1;
+            j["ksuf_capacity"] += lf->ksuf_capacity();
+            j["ksuf_len"] += active_ksuf_len;
+            j["ksuf_by_layer"][layer] += 1;
+            if (!active_ksuf_len) {
+                j["unused_ksuf_capacity"] += lf->ksuf_capacity();
+                j["unused_ksuf_by_layer"][layer] += 1;
+                if (lf->ksuf_external())
+                    j["unused_ksuf_external"] += 1;
+            } else
+                j["used_ksuf_by_layer"][layer] += 1;
+        }
+    } else {
+        internode<P> *in = static_cast<internode<P> *>(n);
+        for (int i = 0; i <= n->size(); ++i)
+            if (in->child_[i])
+                json_stats1(in->child_[i], j, layer, depth + 1, ti);
+        j[&"l1_node_by_depth"[!layer * 3]][depth] += 1;
+    }
+}
+
+template <typename P>
+void query_table<P>::json_stats(lcdf::Json& j, threadinfo& ti)
+{
+    using lcdf::Json;
+    j["size"] = 0.0;
+    j["l1_count"] = 0;
+    j["l1_size"] = 0;
+    const char* jarrays[] = {
+        "node_by_depth", "leaf_by_depth", "leaf_by_size",
+        "l1_node_by_depth", "l1_leaf_by_depth", "l1_leaf_by_size",
+        "key_by_layer", "key_by_length",
+        "ksuf_by_layer", "unused_ksuf_by_layer", "used_ksuf_by_layer"
+    };
+    for (const char** x = jarrays; x != jarrays + sizeof(jarrays) / sizeof(*jarrays); ++x)
+        j[*x] = Json::make_array();
+
+    json_stats1(table_.root(), j, 0, 0, ti);
+
+    j.unset("l1_size");
+    for (const char** x = jarrays; x != jarrays + sizeof(jarrays) / sizeof(*jarrays); ++x) {
+        Json& a = j[*x];
+        for (Json* it = a.array_data(); it != a.end_array_data(); ++it)
+            if (!*it)
+                *it = Json((size_t) 0);
+        if (a.empty())
+            j.unset(*x);
+    }
+}
+
+template <typename N>
+static Str findpv(N *n, int pvi, int npv)
+{
+    // XXX assumes that most keys differ in the low bytes
+    // XXX will fail badly if all keys have the same prefix
+    // XXX not clear what to do then
+    int nbranch = 1, branchid = 0;
+    typedef typename N::internode_type internode_type;
+    typedef typename N::leaf_type leaf_type;
+
+    n = n->unsplit_ancestor();
+
+    while (1) {
+        typename N::nodeversion_type v = n->stable();
+        int size = n->size() + !n->isleaf();
+        if (size == 0)
+            return Str();
+
+        int total_nkeys_estimate = nbranch * size;
+        int first_pv_in_node = branchid * size;
+        int pv_offset = pvi * total_nkeys_estimate / npv - first_pv_in_node;
+
+        if (!n->isleaf() && total_nkeys_estimate < npv) {
+            internode_type *in = static_cast<internode_type *>(n);
+            pv_offset = std::min(std::max(pv_offset, 0), size - 1);
+            N *next = in->child_[pv_offset];
+            if (!n->has_changed(v)) {
+                nbranch = total_nkeys_estimate;
+                branchid = first_pv_in_node + pv_offset;
+                n = next;
+            }
+            continue;
+        }
+
+        pv_offset = std::min(std::max(pv_offset, 0), size - 1 - !n->isleaf());
+        typename N::ikey_type ikey0;
+        if (n->isleaf()) {
+            leaf_type *l = static_cast<leaf_type *>(n);
+            typename leaf_type::permuter_type perm = l->permutation();
+            ikey0 = l->ikey0_[perm[pv_offset]];
+        } else {
+            internode_type *in = static_cast<internode_type *>(n);
+            ikey0 = in->ikey0_[pv_offset];
+        }
+
+        if (!n->has_changed(v)) {
+            char *x = (char *) malloc(sizeof(ikey0));
+            int len = string_slice<typename N::ikey_type>::unparse_comparable(x, sizeof(ikey0), ikey0);
+            return Str(x, len);
+        }
+    }
+}
+
+// findpivots should allocate memory for pv[i]->s, which will be
+// freed by the caller.
+template <typename P>
+void query_table<P>::findpivots(Str *pv, int npv) const
+{
+    pv[0].assign(NULL, 0);
+    char *cmaxk = (char *)malloc(MASSTREE_MAXKEYLEN);
+    memset(cmaxk, 255, MASSTREE_MAXKEYLEN);
+    pv[npv - 1].assign(cmaxk, MASSTREE_MAXKEYLEN);
+    for (int i = 1; i < npv - 1; i++)
+        pv[i] = findpv(table_.root(), i, npv - 1);
+}
+
+namespace {
+struct scan_tester {
+    const char * const *vbegin_, * const *vend_;
+    char key_[32];
+    int keylen_;
+    bool reverse_;
+    bool first_;
+    scan_tester(const char * const *vbegin, const char * const *vend,
+                bool reverse = false)
+        : vbegin_(vbegin), vend_(vend), keylen_(0), reverse_(reverse),
+          first_(true) {
+        if (reverse_) {
+            memset(key_, 255, sizeof(key_));
+            keylen_ = sizeof(key_);
+        }
+    }
+    template <typename SS, typename K>
+    void visit_leaf(const SS&, const K&, threadinfo&) {
+    }
+    bool visit_value(Str key, row_type*, threadinfo&) {
+        memcpy(key_, key.s, key.len);
+        keylen_ = key.len;
+        const char *pos = (reverse_ ? vend_[-1] : vbegin_[0]);
+        if ((int) strlen(pos) != key.len || memcmp(pos, key.s, key.len) != 0) {
+            fprintf(stderr, "%sscan encountered %.*s, expected %s\n", reverse_ ? "r" : "", key.len, key.s, pos);
+            assert((int) strlen(pos) == key.len && memcmp(pos, key.s, key.len) == 0);
+        }
+        fprintf(stderr, "%sscan %.*s\n", reverse_ ? "r" : "", key.len, key.s);
+        (reverse_ ? --vend_ : ++vbegin_);
+        first_ = false;
+        return vbegin_ != vend_;
+    }
+    template <typename T>
+    int scan(T& table, threadinfo& ti) {
+        return table.table().scan(Str(key_, keylen_), first_, *this, ti);
+    }
+    template <typename T>
+    int rscan(T& table, threadinfo& ti) {
+        return table.table().rscan(Str(key_, keylen_), first_, *this, ti);
+    }
+};
+}
+
+template <typename P>
+void query_table<P>::test(threadinfo& ti) {
+    query_table<P> t;
+    t.initialize(ti);
+    query<row_type> q;
+
+    const char * const values[] = {
+        "", "0", "1", "10", "100000000",                        // 0-4
+        "1000000001", "1000000002", "2", "20", "200000000",     // 5-9
+        "aaaaaaaaaaaaaaaaaaaaaaaaaa",                           // 10
+        "aaaaaaaaaaaaaaabbbb", "aaaaaaaaaaaaaaabbbc", "aaaaaaaaaxaaaaabbbc", "b", "c", "d", "e", "f", "g", "h", "i", "j",
+        "kkkkkkkk\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" "a",
+        "kkkkkkkk\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" "b",
+        "xxxxxxxxy"
+    };
+    const char * const *end_values = values + arraysize(values);
+    const char *values_copy[arraysize(values)];
+    memcpy(values_copy, values, sizeof(values));
+
+    for (int i = arraysize(values); i > 0; --i) {
+        int x = rand() % i;
+        q.run_replace(t.table(), Str(values_copy[x]), Str(values_copy[x]), ti);
+        values_copy[x] = values_copy[i - 1];
+    }
+
+    t.table_.print();
+    printf("\n");
+
+    scan_tester scanner(values, values + 3);
+    while (scanner.scan(t, ti)) {
+        scanner.vend_ = std::min(scanner.vend_ + 3, end_values);
+        fprintf(stderr, "-scanbreak-\n");
+    }
+
+    scanner = scan_tester(values, values + 8);
+    while (scanner.scan(t, ti)) {
+        scanner.vend_ = std::min(scanner.vend_ + 8, end_values);
+        fprintf(stderr, "-scanbreak-\n");
+    }
+
+    scanner = scan_tester(values + 10, values + 11);
+    int r = t.table_.scan(Str(values[10]), true, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 10, values + 11);
+    r = t.table_.scan(Str(values[10] + 1), true, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 11, values + 12);
+    r = t.table_.scan(Str(values[10]), false, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 10, values + 11);
+    r = t.table_.scan(Str("aaaaaaaaaaaaaaaaaaaaaaaaaZ"), true, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 11, values + 12);
+    r = t.table_.scan(Str(values[11]), true, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 12, values + 13);
+    r = t.table_.scan(Str(values[11]), false, scanner, ti);
+    always_assert(r == 1);
+
+
+    scanner = scan_tester(end_values - 3, end_values, true);
+    while (scanner.rscan(t, ti)) {
+        scanner.vbegin_ = std::max(scanner.vbegin_ - 3, (const char * const *) values);
+        fprintf(stderr, "-scanbreak-\n");
+    }
+
+    scanner = scan_tester(end_values - 2, end_values, true);
+    r = scanner.rscan(t, ti);
+    always_assert(r == 2);
+    scanner.vbegin_ = std::max(scanner.vbegin_ - 2, (const char * const *) values);
+    fprintf(stderr, "-scanbreak-\n");
+    r = scanner.rscan(t, ti);
+    always_assert(r == 2);
+
+    scanner = scan_tester(end_values - 8, end_values, true);
+    while (scanner.rscan(t, ti)) {
+        scanner.vbegin_ = std::max(scanner.vbegin_ - 8, (const char * const *) values);
+        fprintf(stderr, "-scanbreak-\n");
+    }
+
+    scanner = scan_tester(values + 10, values + 11);
+    r = t.table_.rscan(Str(values[10]), true, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 10, values + 11);
+    r = t.table_.rscan(Str("aaaaaaaaaaaaaaaaaaaaaaaaab"), true, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 9, values + 10);
+    r = t.table_.rscan(Str(values[10]), false, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 10, values + 11);
+    r = t.table_.rscan(Str("aaaaaaaaaaaaaaaaaaaaaaaaab"), true, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 11, values + 12);
+    r = t.table_.rscan(Str(values[11]), true, scanner, ti);
+    always_assert(r == 1);
+
+    scanner = scan_tester(values + 10, values + 11);
+    r = t.table_.rscan(Str(values[11]), false, scanner, ti);
+    always_assert(r == 1);
+
+
+    Str pv[10];
+    t.findpivots(pv, 10);
+    for (int i = 0; i < 10; ++i) {
+        fprintf(stderr, "%d >%.*s<\n", i, std::min(pv[i].len, 10), pv[i].s);
+        free((char *)pv[i].s);
+    }
+    t.findpivots(pv, 4);
+    for (int i = 0; i < 4; ++i) {
+        fprintf(stderr, "%d >%.*s<\n", i, std::min(pv[i].len, 10), pv[i].s);
+        free((char *)pv[i].s);
+    }
+
+    // XXX destroy tree
+}
+
+template <typename P>
+void query_table<P>::print(FILE *f, int indent) const {
+    table_.print(f, indent);
+}
+
+template class basic_table<default_table::param_type>;
+template class query_table<default_table::param_type>;
+
+}
diff --git a/silo/masstree/query_masstree.hh b/silo/masstree/query_masstree.hh
new file mode 100644 (file)
index 0000000..b59f3e5
--- /dev/null
@@ -0,0 +1,82 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef QUERY_MASSTREE_HH
+#define QUERY_MASSTREE_HH 1
+#include "masstree.hh"
+#include "kvrow.hh"
+class threadinfo;
+namespace lcdf { class Json; }
+
+namespace Masstree {
+
+template <typename P>
+class query_table {
+  public:
+    typedef P param_type;
+    typedef node_base<P> node_type;
+    typedef typename P::threadinfo_type threadinfo;
+    typedef unlocked_tcursor<P> unlocked_cursor_type;
+    typedef tcursor<P> cursor_type;
+
+    query_table() {
+    }
+
+    const basic_table<P>& table() const {
+        return table_;
+    }
+    basic_table<P>& table() {
+        return table_;
+    }
+
+    void initialize(threadinfo& ti) {
+        table_.initialize(ti);
+    }
+    void destroy(threadinfo& ti) {
+        table_.destroy(ti);
+    }
+
+    void findpivots(Str* pv, int npv) const;
+
+    void stats(FILE* f);
+    void json_stats(lcdf::Json& j, threadinfo& ti);
+    inline lcdf::Json json_stats(threadinfo& ti) {
+        lcdf::Json j;
+        json_stats(j, ti);
+        return j;
+    }
+
+    void print(FILE* f, int indent) const;
+
+    static void test(threadinfo& ti);
+
+    static const char* name() {
+        return "mb";
+    }
+
+  private:
+    basic_table<P> table_;
+};
+
+struct default_query_table_params : public nodeparams<15, 15> {
+    typedef row_type* value_type;
+    typedef value_print<value_type> value_print_type;
+    typedef ::threadinfo threadinfo_type;
+};
+
+typedef query_table<default_query_table_params> default_table;
+
+} // namespace Masstree
+#endif
diff --git a/silo/masstree/str.cc b/silo/masstree/str.cc
new file mode 100644 (file)
index 0000000..ac11269
--- /dev/null
@@ -0,0 +1,53 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "str.hh"
+namespace lcdf {
+
+const Str Str::maxkey("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+                     "\xFF", 257);
+
+} // namespace lcdf
diff --git a/silo/masstree/str.hh b/silo/masstree/str.hh
new file mode 100644 (file)
index 0000000..abdc4d9
--- /dev/null
@@ -0,0 +1,165 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef STR_HH
+#define STR_HH
+#include "string_base.hh"
+#include <stdarg.h>
+#include <stdio.h>
+namespace lcdf {
+
+struct Str : public String_base<Str> {
+    typedef Str substring_type;
+    typedef Str argument_type;
+
+    const char *s;
+    int len;
+
+    Str()
+       : s(0), len(0) {
+    }
+    template <typename T>
+    Str(const String_base<T>& x)
+       : s(x.data()), len(x.length()) {
+    }
+    Str(const char* s_)
+       : s(s_), len(strlen(s_)) {
+    }
+    Str(const char* s_, int len_)
+       : s(s_), len(len_) {
+    }
+    Str(const unsigned char* s_, int len_)
+       : s(reinterpret_cast<const char*>(s_)), len(len_) {
+    }
+    Str(const char *first, const char *last)
+       : s(first), len(last - first) {
+        precondition(first <= last);
+    }
+    Str(const unsigned char *first, const unsigned char *last)
+       : s(reinterpret_cast<const char*>(first)), len(last - first) {
+        precondition(first <= last);
+    }
+    Str(const std::string& str)
+        : s(str.data()), len(str.length()) {
+    }
+    Str(const uninitialized_type &unused) {
+       (void) unused;
+    }
+
+    static const Str maxkey;
+
+    void assign() {
+       s = 0;
+       len = 0;
+    }
+    template <typename T>
+    void assign(const String_base<T> &x) {
+       s = x.data();
+       len = x.length();
+    }
+    void assign(const char *s_) {
+       s = s_;
+       len = strlen(s_);
+    }
+    void assign(const char *s_, int len_) {
+       s = s_;
+       len = len_;
+    }
+
+    const char *data() const {
+       return s;
+    }
+    int length() const {
+       return len;
+    }
+    char* mutable_data() {
+        return const_cast<char*>(s);
+    }
+
+    Str prefix(int lenx) const {
+        return Str(s, lenx < len ? lenx : len);
+    }
+    Str substring(const char *first, const char *last) const {
+       if (first <= last && first >= s && last <= s + len)
+           return Str(first, last);
+       else
+           return Str();
+    }
+    Str substring(const unsigned char *first, const unsigned char *last) const {
+       const unsigned char *u = reinterpret_cast<const unsigned char*>(s);
+       if (first <= last && first >= u && last <= u + len)
+           return Str(first, last);
+       else
+           return Str();
+    }
+    Str fast_substring(const char *first, const char *last) const {
+       assert(begin() <= first && first <= last && last <= end());
+       return Str(first, last);
+    }
+    Str fast_substring(const unsigned char *first, const unsigned char *last) const {
+       assert(ubegin() <= first && first <= last && last <= uend());
+       return Str(first, last);
+    }
+    Str ltrim() const {
+       return String_generic::ltrim(*this);
+    }
+    Str rtrim() const {
+       return String_generic::rtrim(*this);
+    }
+    Str trim() const {
+       return String_generic::trim(*this);
+    }
+
+    long to_i() const {                // XXX does not handle negative
+       long x = 0;
+       int p;
+       for (p = 0; p < len && s[p] >= '0' && s[p] <= '9'; ++p)
+           x = (x * 10) + s[p] - '0';
+       return p == len && p != 0 ? x : -1;
+    }
+
+    static Str snprintf(char *buf, size_t size, const char *fmt, ...) {
+       va_list val;
+       va_start(val, fmt);
+       int n = vsnprintf(buf, size, fmt, val);
+       va_end(val);
+       return Str(buf, n);
+    }
+};
+
+struct inline_string : public String_base<inline_string> {
+    int len;
+    char s[0];
+
+    const char *data() const {
+       return s;
+    }
+    int length() const {
+       return len;
+    }
+
+    size_t size() const {
+       return sizeof(inline_string) + len;
+    }
+    static size_t size(int len) {
+       return sizeof(inline_string) + len;
+    }
+};
+
+} // namespace lcdf
+
+LCDF_MAKE_STRING_HASH(lcdf::Str)
+LCDF_MAKE_STRING_HASH(lcdf::inline_string)
+#endif
diff --git a/silo/masstree/straccum.cc b/silo/masstree/straccum.cc
new file mode 100644 (file)
index 0000000..15bbb77
--- /dev/null
@@ -0,0 +1,398 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+/*
+ * straccum.{cc,hh} -- build up strings with operator<<
+ * Eddie Kohler
+ *
+ * Copyright (c) 1999-2000 Massachusetts Institute of Technology
+ * Copyright (c) 2001-2013 Eddie Kohler
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file
+ * is legally binding.
+ */
+
+#include "straccum.hh"
+#include <stdarg.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+namespace lcdf {
+
+/** @class StringAccum
+ * @brief Efficiently build up Strings from pieces.
+ *
+ * Like the String class, StringAccum represents a string of characters.
+ * However, unlike a String, a StringAccum is inherently mutable, and
+ * efficiently supports building up a large string from many smaller pieces.
+ *
+ * StringAccum objects support operator<<() operations for most fundamental
+ * data types.  A StringAccum is generally built up by operator<<(), and then
+ * turned into a String by the take_string() method.  Extracting the String
+ * from a StringAccum does no memory allocation or copying; the StringAccum's
+ * memory is donated to the String.
+ *
+ * <h3>Out-of-memory StringAccums</h3>
+ *
+ * When there is not enough memory to add requested characters to a
+ * StringAccum object, the object becomes a special "out-of-memory"
+ * StringAccum. Out-of-memory objects are contagious: the result of any
+ * concatenation operation involving an out-of-memory StringAccum is another
+ * out-of-memory StringAccum. Calling take_string() on an out-of-memory
+ * StringAccum returns an out-of-memory String.
+ *
+ * Note that appending an out-of-memory String to a StringAccum <em>does
+ * not</em> make the StringAccum out-of-memory.
+ */
+
+/** @brief Change this StringAccum into an out-of-memory StringAccum. */
+void
+StringAccum::assign_out_of_memory()
+{
+    if (r_.cap > 0)
+       delete[] reinterpret_cast<char*>(r_.s - memo_space);
+    r_.s = reinterpret_cast<unsigned char*>(const_cast<char*>(String_generic::empty_data));
+    r_.cap = -1;
+    r_.len = 0;
+}
+
+char* StringAccum::grow(int ncap) {
+    // can't append to out-of-memory strings
+    if (r_.cap < 0) {
+       errno = ENOMEM;
+       return 0;
+    }
+
+    if (ncap < r_.cap)
+        return reinterpret_cast<char*>(r_.s + r_.len);
+    else if (ncap < 128)
+        ncap = 128;
+    else if (r_.cap < (1 << 20) && ncap < (r_.cap + memo_space) * 2 - memo_space)
+        ncap = (r_.cap + memo_space) * 2 - memo_space;
+    else if (r_.cap >= (1 << 20) && ncap < r_.cap + (1 << 19))
+        ncap = r_.cap + (1 << 19);
+
+    char* n = new char[ncap + memo_space];
+    if (!n) {
+       assign_out_of_memory();
+       errno = ENOMEM;
+       return 0;
+    }
+
+    n += memo_space;
+    if (r_.cap > 0) {
+       memcpy(n, r_.s, r_.len);
+       delete[] reinterpret_cast<char*>(r_.s - memo_space);
+    }
+    r_.s = reinterpret_cast<unsigned char*>(n);
+    r_.cap = ncap;
+    return reinterpret_cast<char*>(r_.s + r_.len);
+}
+
+/** @brief Set the StringAccum's length to @a len.
+    @pre @a len >= 0
+    @return 0 on success, -ENOMEM on failure */
+int
+StringAccum::resize(int len)
+{
+    assert(len >= 0);
+    if (len > r_.cap && !grow(len))
+       return -ENOMEM;
+    else {
+       r_.len = len;
+       return 0;
+    }
+}
+
+char *
+StringAccum::hard_extend(int nadjust, int nreserve)
+{
+    char *x;
+    if (r_.len + nadjust + nreserve <= r_.cap)
+       x = reinterpret_cast<char*>(r_.s + r_.len);
+    else
+       x = grow(r_.len + nadjust + nreserve);
+    if (x)
+       r_.len += nadjust;
+    return x;
+}
+
+void StringAccum::transfer_from(String& x) {
+    if (x.is_shared() || x._r.memo_offset != -memo_space) {
+        append(x.begin(), x.end());
+        x._r.deref();
+    } else {
+        r_.s = const_cast<unsigned char*>(x.udata());
+        r_.len = x.length();
+        r_.cap = x._r.memo()->capacity;
+    }
+}
+
+/** @brief Null-terminate this StringAccum and return its data.
+
+    Note that the null character does not contribute to the StringAccum's
+    length(), and later append() and similar operations can overwrite it. If
+    appending the null character fails, the StringAccum becomes
+    out-of-memory and the returned value is a null string. */
+const char *
+StringAccum::c_str()
+{
+    if (r_.len < r_.cap || grow(r_.len))
+       r_.s[r_.len] = '\0';
+    return reinterpret_cast<char *>(r_.s);
+}
+
+/** @brief Append @a len copies of character @a c to the StringAccum. */
+void
+StringAccum::append_fill(int c, int len)
+{
+    if (char *s = extend(len))
+       memset(s, c, len);
+}
+
+void
+StringAccum::hard_append(const char *s, int len)
+{
+    // We must be careful about calls like "sa.append(sa.begin(), sa.end())";
+    // a naive implementation might use sa's data after freeing it.
+    const char *my_s = reinterpret_cast<char *>(r_.s);
+
+    if (r_.len + len <= r_.cap) {
+    success:
+       memcpy(r_.s + r_.len, s, len);
+       r_.len += len;
+    } else if (likely(s < my_s || s >= my_s + r_.cap)) {
+       if (grow(r_.len + len))
+           goto success;
+    } else {
+       rep_t old_r = r_;
+       r_ = rep_t();
+       if (char *new_s = extend(old_r.len + len)) {
+           memcpy(new_s, old_r.s, old_r.len);
+           memcpy(new_s + old_r.len, s, len);
+       }
+       delete[] reinterpret_cast<char*>(old_r.s - memo_space);
+    }
+}
+
+void
+StringAccum::hard_append_cstr(const char *cstr)
+{
+    append(cstr, strlen(cstr));
+}
+
+bool
+StringAccum::append_utf8_hard(int ch)
+{
+    if (ch < 0x8000) {
+       append(static_cast<char>(0xC0 | (ch >> 6)));
+       goto char1;
+    } else if (ch < 0x10000) {
+       if (unlikely((ch >= 0xD800 && ch < 0xE000) || ch > 0xFFFD))
+           return false;
+       append(static_cast<char>(0xE0 | (ch >> 12)));
+       goto char2;
+    } else if (ch < 0x110000) {
+       append(static_cast<char>(0xF0 | (ch >> 18)));
+       append(static_cast<char>(0x80 | ((ch >> 12) & 0x3F)));
+    char2:
+       append(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
+    char1:
+       append(static_cast<char>(0x80 | (ch & 0x3F)));
+    } else
+       return false;
+    return true;
+}
+
+/** @brief Return a String object with this StringAccum's contents.
+
+    This operation donates the StringAccum's memory to the returned String.
+    After a call to take_string(), the StringAccum object becomes empty, and
+    any future append() operations may cause memory allocations. If the
+    StringAccum is out-of-memory, the returned String is also out-of-memory,
+    but the StringAccum's out-of-memory state is reset. */
+String
+StringAccum::take_string()
+{
+    int len = length();
+    int cap = r_.cap;
+    char* str = reinterpret_cast<char*>(r_.s);
+    if (len > 0 && cap > 0) {
+        String::memo_type* memo =
+            reinterpret_cast<String::memo_type*>(r_.s - memo_space);
+        memo->initialize(cap, len);
+       r_ = rep_t();
+       return String(str, len, memo);
+    } else if (!out_of_memory())
+       return String();
+    else {
+       clear();
+       return String::make_out_of_memory();
+    }
+}
+
+/** @brief Swap this StringAccum's contents with @a x. */
+void
+StringAccum::swap(StringAccum &x)
+{
+    rep_t xr = x.r_;
+    x.r_ = r_;
+    r_ = xr;
+}
+
+/** @relates StringAccum
+    @brief Append decimal representation of @a i to @a sa.
+    @return @a sa */
+StringAccum &
+operator<<(StringAccum &sa, long i)
+{
+    if (char *x = sa.reserve(24)) {
+       int len = sprintf(x, "%ld", i);
+       sa.adjust_length(len);
+    }
+    return sa;
+}
+
+/** @relates StringAccum
+    @brief Append decimal representation of @a u to @a sa.
+    @return @a sa */
+StringAccum &
+operator<<(StringAccum &sa, unsigned long u)
+{
+    if (char *x = sa.reserve(24)) {
+       int len = sprintf(x, "%lu", u);
+       sa.adjust_length(len);
+    }
+    return sa;
+}
+
+/** @relates StringAccum
+    @brief Append decimal representation of @a i to @a sa.
+    @return @a sa */
+StringAccum &
+operator<<(StringAccum &sa, long long i)
+{
+    if (char *x = sa.reserve(24)) {
+       int len = sprintf(x, "%lld", i);
+       sa.adjust_length(len);
+    }
+    return sa;
+}
+
+/** @relates StringAccum
+    @brief Append decimal representation of @a u to @a sa.
+    @return @a sa */
+StringAccum &
+operator<<(StringAccum &sa, unsigned long long u)
+{
+    if (char *x = sa.reserve(24)) {
+       int len = sprintf(x, "%llu", u);
+       sa.adjust_length(len);
+    }
+    return sa;
+}
+
+StringAccum &
+operator<<(StringAccum &sa, double d)
+{
+    if (char *x = sa.reserve(256)) {
+       int len = sprintf(x, "%.12g", d);
+       sa.adjust_length(len);
+    }
+    return sa;
+}
+
+/** @brief Append result of vsnprintf() to this StringAccum.
+    @param n maximum number of characters to print
+    @param format format argument to snprintf()
+    @param val argument set
+    @return *this
+    @sa snprintf */
+StringAccum &
+StringAccum::vsnprintf(int n, const char *format, va_list val)
+{
+    if (char *x = reserve(n + 1)) {
+#if HAVE_VSNPRINTF
+       int len = ::vsnprintf(x, n + 1, format, val);
+#else
+       int len = vsprintf(x, format, val);
+       assert(len <= n);
+#endif
+       adjust_length(len);
+    }
+    return *this;
+}
+
+/** @brief Append result of snprintf() to this StringAccum.
+    @param n maximum number of characters to print
+    @param format format argument to snprintf()
+    @return *this
+
+    The terminating null character is not appended to the string.
+
+    @note The safe vsnprintf() variant is called if it exists. It does in
+    the Linux kernel, and on modern Unix variants. However, if it does not
+    exist on your machine, then this function is actually unsafe, and you
+    should make sure that the printf() invocation represented by your
+    arguments will never write more than @a n characters, not including the
+    terminating null.
+    @sa vsnprintf */
+StringAccum &
+StringAccum::snprintf(int n, const char *format, ...)
+{
+    va_list val;
+    va_start(val, format);
+    vsnprintf(n, format, val);
+    va_end(val);
+    return *this;
+}
+
+void
+StringAccum::append_break_lines(const String& text, int linelen, const String &leftmargin)
+{
+    if (text.length() == 0)
+       return;
+    const char* line = text.begin();
+    const char* ends = text.end();
+    linelen -= leftmargin.length();
+    for (const char* s = line; s < ends; s++) {
+       const char* start = s;
+       while (s < ends && isspace((unsigned char) *s))
+           s++;
+       const char* word = s;
+       while (s < ends && !isspace((unsigned char) *s))
+           s++;
+       if (s - line > linelen && start > line) {
+           *this << leftmargin;
+           append(line, start - line);
+           *this << '\n';
+           line = word;
+       }
+    }
+    if (line < text.end()) {
+       *this << leftmargin;
+       append(line, text.end() - line);
+       *this << '\n';
+    }
+}
+
+} // namespace lcdf
diff --git a/silo/masstree/straccum.hh b/silo/masstree/straccum.hh
new file mode 100644 (file)
index 0000000..8ff0636
--- /dev/null
@@ -0,0 +1,736 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef LCDF_STRACCUM_HH
+#define LCDF_STRACCUM_HH
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include "string.hh"
+#if __GNUC__ > 4
+# define LCDF_SNPRINTF_ATTR __attribute__((__format__(__printf__, 3, 4)))
+#else
+# define LCDF_SNPRINTF_ATTR /* nothing */
+#endif
+namespace lcdf {
+
+/** @file <lcdf/straccum.hh>
+    @brief Click's StringAccum class, used to construct Strings efficiently from pieces.
+*/
+
+class StringAccum { public:
+
+    typedef const char *const_iterator;
+    typedef char *iterator;
+
+    typedef int (StringAccum::*unspecified_bool_type)() const;
+
+    inline StringAccum();
+    explicit inline StringAccum(int capacity);
+    explicit inline StringAccum(const char* cstr);
+    inline StringAccum(const char* s, int len);
+    template <typename T>
+    inline StringAccum(const String_base<T>& str);
+    inline StringAccum(const StringAccum& x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline StringAccum(StringAccum&& x);
+    inline StringAccum(String&& x);
+#endif
+    inline ~StringAccum();
+    static inline StringAccum make_transfer(String& x);
+
+    inline StringAccum &operator=(const StringAccum& x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline StringAccum &operator=(StringAccum&& x);
+#endif
+
+    inline const char* data() const;
+    inline char* data();
+    inline const unsigned char* udata() const;
+    inline unsigned char* udata();
+    inline int length() const;
+    inline int capacity() const;
+
+    const char *c_str();
+
+    inline operator unspecified_bool_type() const;
+    inline bool empty() const;
+    inline bool operator!() const;
+
+    inline const_iterator begin() const;
+    inline iterator begin();
+    inline const_iterator end() const;
+    inline iterator end();
+
+    inline char operator[](int i) const;
+    inline char &operator[](int i);
+    inline char front() const;
+    inline char &front();
+    inline char back() const;
+    inline char &back();
+
+    inline bool out_of_memory() const;
+    void assign_out_of_memory();
+
+    inline void clear();
+    inline char *reserve(int n);
+    inline void set_length(int len);
+    int resize(int len);
+    inline void adjust_length(int delta);
+    inline void set_end(char* end);
+    inline void set_end(unsigned char* end);
+    inline char *extend(int nadjust, int nreserve = 0);
+
+    inline void pop_back(int n = 1);
+
+    inline void append(char c);
+    inline void append(unsigned char c);
+    inline bool append_utf8(int ch);
+    inline void append(const char *cstr);
+    inline void append(const char *s, int len);
+    inline void append(const unsigned char *s, int len);
+    inline void append(const char *first, const char *last);
+    inline void append(const unsigned char *first, const unsigned char *last);
+    void append_fill(int c, int len);
+
+    template <typename T>
+    void append_encoded(T &state, const unsigned char *first,
+                       const unsigned char *last);
+    template <typename T>
+    inline void append_encoded(T &state, const char *first, const char *last);
+    template <typename T>
+    void append_encoded(T &state);
+    template <typename T>
+    inline void append_encoded(const unsigned char *first, const unsigned char *last);
+    template <typename T>
+    inline void append_encoded(const char *first, const char *last);
+
+    // word joining
+    template <typename I>
+    inline void append_join(const String &joiner, I first, I last);
+    template <typename T>
+    inline void append_join(const String &joiner, const T &x);
+    void append_break_lines(const String &text, int linelen, const String &leftmargin = String());
+
+    StringAccum &snprintf(int n, const char *format, ...) LCDF_SNPRINTF_ATTR;
+    StringAccum &vsnprintf(int n, const char *format, va_list val);
+
+    String take_string();
+
+    void swap(StringAccum& x);
+
+    // see also operator<< declarations below
+
+  private:
+
+    enum {
+       memo_space = String::MEMO_SPACE
+    };
+
+    struct rep_t {
+       unsigned char *s;
+       int len;
+       int cap;
+       rep_t()
+           : s(reinterpret_cast<unsigned char *>(const_cast<char *>(String_generic::empty_data))),
+             len(0), cap(0) {
+       }
+       explicit rep_t(uninitialized_type) {
+       }
+    };
+
+    rep_t r_;
+
+    char* grow(int ncap);
+    char* hard_extend(int nadjust, int nreserve);
+    void hard_append(const char *s, int len);
+    void hard_append_cstr(const char *cstr);
+    bool append_utf8_hard(int ch);
+    void transfer_from(String& x);
+};
+
+inline StringAccum &operator<<(StringAccum &sa, char c);
+inline StringAccum &operator<<(StringAccum &sa, unsigned char c);
+inline StringAccum &operator<<(StringAccum &sa, const char *cstr);
+template <typename T> inline StringAccum &operator<<(StringAccum &sa, const String_base<T> &str);
+inline StringAccum &operator<<(StringAccum &sa, const StringAccum &x);
+
+inline StringAccum &operator<<(StringAccum &sa, bool x);
+inline StringAccum &operator<<(StringAccum &sa, short x);
+inline StringAccum &operator<<(StringAccum &sa, unsigned short x);
+inline StringAccum &operator<<(StringAccum &sa, int x);
+inline StringAccum &operator<<(StringAccum &sa, unsigned x);
+StringAccum& operator<<(StringAccum& sa, long x);
+StringAccum& operator<<(StringAccum& sa, unsigned long x);
+StringAccum& operator<<(StringAccum& sa, long long x);
+StringAccum& operator<<(StringAccum& sa, unsigned long long x);
+StringAccum& operator<<(StringAccum& sa, double x);
+
+/** @brief Construct an empty StringAccum (with length 0). */
+inline StringAccum::StringAccum() {
+}
+
+/** @brief Construct a StringAccum with room for at least @a capacity
+    characters.
+    @param capacity initial capacity
+
+    If @a capacity == 0, the StringAccum is created empty. If @a capacity is
+    too large (so that @a capacity bytes of memory can't be allocated), the
+    StringAccum falls back to a smaller capacity (possibly zero). */
+inline StringAccum::StringAccum(int capacity) {
+    assert(capacity >= 0);
+    grow(capacity);
+}
+
+/** @brief Construct a StringAccum containing the characters in @a cstr. */
+inline StringAccum::StringAccum(const char *cstr) {
+    append(cstr);
+}
+
+/** @brief Construct a StringAccum containing the characters in @a s. */
+inline StringAccum::StringAccum(const char *s, int len) {
+    append(s, len);
+}
+
+/** @brief Construct a StringAccum containing the characters in @a str. */
+template <typename T>
+inline StringAccum::StringAccum(const String_base<T> &str) {
+    append(str.begin(), str.end());
+}
+
+/** @brief Construct a StringAccum containing a copy of @a x. */
+inline StringAccum::StringAccum(const StringAccum &x) {
+    append(x.data(), x.length());
+}
+
+#if HAVE_CXX_RVALUE_REFERENCES
+/** @brief Move-construct a StringAccum from @a x. */
+inline StringAccum::StringAccum(StringAccum&& x) {
+    using std::swap;
+    swap(r_, x.r_);
+}
+
+inline StringAccum::StringAccum(String&& x) {
+    transfer_from(x);
+    x._r = String::rep_type{String_generic::empty_data, 0, 0};
+}
+#endif
+
+/** @brief Destroy a StringAccum, freeing its memory. */
+inline StringAccum::~StringAccum() {
+    if (r_.cap > 0)
+       delete[] reinterpret_cast<char*>(r_.s - memo_space);
+}
+
+inline StringAccum StringAccum::make_transfer(String& x) {
+    StringAccum sa;
+    sa.transfer_from(x);
+    x._r = String::rep_type{String_generic::empty_data, 0, 0};
+    return sa;
+}
+
+/** @brief Return the contents of the StringAccum.
+
+    The returned data() value points to length() bytes of writable memory. */
+inline const char* StringAccum::data() const {
+    return reinterpret_cast<const char*>(r_.s);
+}
+
+/** @overload */
+inline char* StringAccum::data() {
+    return reinterpret_cast<char*>(r_.s);
+}
+
+/** @brief Return the contents of the StringAccum.
+
+    The returned data() value points to length() bytes of writable memory. */
+inline const unsigned char* StringAccum::udata() const {
+    return r_.s;
+}
+
+/** @overload */
+inline unsigned char* StringAccum::udata() {
+    return r_.s;
+}
+
+/** @brief Return the length of the StringAccum. */
+inline int StringAccum::length() const {
+    return r_.len;
+}
+
+/** @brief Return the StringAccum's current capacity.
+
+    The capacity is the maximum length the StringAccum can hold without
+    incurring a memory allocation. Returns -1 for out-of-memory
+    StringAccums. */
+inline int StringAccum::capacity() const {
+    return r_.cap;
+}
+
+/** @brief Return an iterator for the first character in the StringAccum.
+
+    StringAccum iterators are simply pointers into string data, so they are
+    quite efficient. @sa StringAccum::data */
+inline StringAccum::const_iterator StringAccum::begin() const {
+    return reinterpret_cast<char *>(r_.s);
+}
+
+/** @overload */
+inline StringAccum::iterator StringAccum::begin() {
+    return reinterpret_cast<char *>(r_.s);
+}
+
+/** @brief Return an iterator for the end of the StringAccum.
+
+    The return value points one character beyond the last character in the
+    StringAccum. */
+inline StringAccum::const_iterator StringAccum::end() const {
+    return reinterpret_cast<char *>(r_.s + r_.len);
+}
+
+/** @overload */
+inline StringAccum::iterator StringAccum::end() {
+    return reinterpret_cast<char *>(r_.s + r_.len);
+}
+
+/** @brief Test if the StringAccum contains characters. */
+inline StringAccum::operator unspecified_bool_type() const {
+    return r_.len != 0 ? &StringAccum::capacity : 0;
+}
+
+/** @brief Test if the StringAccum is empty.
+
+    Returns true iff length() == 0. */
+inline bool StringAccum::operator!() const {
+    return r_.len == 0;
+}
+
+/** @brief Test if the StringAccum is empty. */
+inline bool StringAccum::empty() const {
+    return r_.len == 0;
+}
+
+/** @brief Test if the StringAccum is out-of-memory. */
+inline bool StringAccum::out_of_memory() const {
+    return unlikely(r_.cap < 0);
+}
+
+/** @brief Return the <a>i</a>th character in the string.
+    @param i character index
+    @pre 0 <= @a i < length() */
+inline char StringAccum::operator[](int i) const {
+    assert((unsigned) i < (unsigned) r_.len);
+    return static_cast<char>(r_.s[i]);
+}
+
+/** @brief Return a reference to the <a>i</a>th character in the string.
+    @param i character index
+    @pre 0 <= @a i < length() */
+inline char &StringAccum::operator[](int i) {
+    assert((unsigned) i < (unsigned) r_.len);
+    return reinterpret_cast<char &>(r_.s[i]);
+}
+
+/** @brief Return the first character in the string.
+    @pre length() > 0 */
+inline char StringAccum::front() const {
+    assert(r_.len > 0);
+    return static_cast<char>(r_.s[0]);
+}
+
+/** @brief Return a reference to the first character in the string.
+    @pre length() > 0 */
+inline char &StringAccum::front() {
+    assert(r_.len > 0);
+    return reinterpret_cast<char &>(r_.s[0]);
+}
+
+/** @brief Return the last character in the string.
+    @pre length() > 0 */
+inline char StringAccum::back() const {
+    assert(r_.len > 0);
+    return static_cast<char>(r_.s[r_.len - 1]);
+}
+
+/** @brief Return a reference to the last character in the string.
+    @pre length() > 0 */
+inline char &StringAccum::back() {
+    assert(r_.len > 0);
+    return reinterpret_cast<char &>(r_.s[r_.len - 1]);
+}
+
+/** @brief Clear the StringAccum's comments.
+
+    All characters in the StringAccum are erased. Also resets the
+    StringAccum's out-of-memory status. */
+inline void StringAccum::clear() {
+    if (r_.cap < 0)
+       r_.cap = 0;
+    r_.len = 0;
+}
+
+/** @brief Reserve space for at least @a n characters.
+    @return a pointer to at least @a n characters, or null if allocation
+    fails
+    @pre @a n >= 0
+
+    reserve() does not change the string's length(), only its capacity(). In
+    a frequent usage pattern, code calls reserve(), passing an upper bound
+    on the characters that could be written by a series of operations. After
+    writing into the returned buffer, adjust_length() is called to account
+    for the number of characters actually written.
+
+    On failure, null is returned and errno is set to ENOMEM. */
+inline char *StringAccum::reserve(int n) {
+    assert(n >= 0);
+    if (r_.len + n <= r_.cap)
+       return reinterpret_cast<char *>(r_.s + r_.len);
+    else
+       return grow(r_.len + n);
+}
+
+/** @brief Set the StringAccum's length to @a len.
+    @param len new length in characters
+    @pre 0 <= @a len <= capacity()
+    @sa adjust_length */
+inline void StringAccum::set_length(int len) {
+    assert(len >= 0 && r_.len <= r_.cap);
+    r_.len = len;
+}
+
+inline void StringAccum::set_end(unsigned char* x) {
+    assert(x >= r_.s && x <= r_.s + r_.cap);
+    r_.len = x - r_.s;
+}
+
+inline void StringAccum::set_end(char* x) {
+    set_end((unsigned char*) x);
+}
+
+/** @brief Adjust the StringAccum's length.
+    @param delta  length adjustment
+    @pre If @a delta > 0, then length() + @a delta <= capacity().
+    If @a delta < 0, then length() + delta >= 0.
+
+    The StringAccum's length after adjust_length(@a delta) equals its old
+    length plus @a delta. Generally adjust_length() is used after a call to
+    reserve(). @sa set_length */
+inline void StringAccum::adjust_length(int delta) {
+    assert(r_.len + delta >= 0 && r_.len + delta <= r_.cap);
+    r_.len += delta;
+}
+
+/** @brief Reserve space and adjust length in one operation.
+    @param nadjust number of characters to reserve and adjust length
+    @param nreserve additional characters to reserve
+    @pre @a nadjust >= 0 and @a nreserve >= 0
+
+    This operation combines the effects of reserve(@a nadjust + @a nreserve)
+    and adjust_length(@a nadjust). Returns the result of the reserve()
+    call. */
+inline char *StringAccum::extend(int nadjust, int nreserve) {
+#if HAVE_OPTIMIZE_SIZE || __OPTIMIZE_SIZE__
+    return hard_extend(nadjust, nreserve);
+#else
+    assert(nadjust >= 0 && nreserve >= 0);
+    if (r_.len + nadjust + nreserve <= r_.cap) {
+       char *x = reinterpret_cast<char *>(r_.s + r_.len);
+       r_.len += nadjust;
+       return x;
+    } else
+       return hard_extend(nadjust, nreserve);
+#endif
+}
+
+/** @brief Remove characters from the end of the StringAccum.
+    @param n number of characters to remove
+    @pre @a n >= 0 and @a n <= length()
+
+    Same as adjust_length(-@a n). */
+inline void StringAccum::pop_back(int n) {
+    assert(n >= 0 && r_.len >= n);
+    r_.len -= n;
+}
+
+/** @brief Append character @a c to the StringAccum.
+    @param c character to append */
+inline void StringAccum::append(char c) {
+    if (r_.len < r_.cap || grow(r_.len))
+       r_.s[r_.len++] = c;
+}
+
+/** @overload */
+inline void StringAccum::append(unsigned char c) {
+    append(static_cast<char>(c));
+}
+
+/** @brief Append the first @a len characters of @a s to this StringAccum.
+    @param s data to append
+    @param len length of data
+    @pre @a len >= 0 */
+inline void StringAccum::append(const char *s, int len) {
+#if HAVE_OPTIMIZE_SIZE || __OPTIMIZE_SIZE__
+    hard_append(s, len);
+#else
+    assert(len >= 0);
+    if (r_.len + len <= r_.cap) {
+       memcpy(r_.s + r_.len, s, len);
+       r_.len += len;
+    } else
+       hard_append(s, len);
+#endif
+}
+
+/** @overload */
+inline void StringAccum::append(const unsigned char *s, int len) {
+    append(reinterpret_cast<const char *>(s), len);
+}
+
+/** @brief Append the null-terminated C string @a s to this StringAccum.
+    @param s data to append */
+inline void StringAccum::append(const char *cstr) {
+    if (LCDF_CONSTANT_CSTR(cstr))
+       append(cstr, strlen(cstr));
+    else
+       hard_append_cstr(cstr);
+}
+
+/** @brief Append the data from @a first to @a last to the end of this
+    StringAccum.
+
+    Does nothing if @a first >= @a last. */
+inline void StringAccum::append(const char *first, const char *last) {
+    if (first < last)
+       append(first, last - first);
+}
+
+/** @overload */
+inline void StringAccum::append(const unsigned char *first, const unsigned char *last) {
+    if (first < last)
+       append(first, last - first);
+}
+
+/** @brief Append Unicode character @a ch encoded in UTF-8.
+    @return true if character was valid.
+
+    Appends nothing if @a ch is not a valid Unicode character. */
+inline bool StringAccum::append_utf8(int ch) {
+    if (unlikely(ch <= 0))
+       return false;
+    else if (likely(ch <= 0x7F)) {
+       append(static_cast<char>(ch));
+       return true;
+    } else
+       return append_utf8_hard(ch);
+}
+
+template <typename T>
+void StringAccum::append_encoded(T &encoder,
+                                const unsigned char *first,
+                                const unsigned char *last)
+{
+    unsigned char *kills = 0;
+    if (first != last)
+       first = encoder.start(first, last);
+    while (1) {
+       encoder.set_output(r_.s + r_.len, r_.s + r_.cap, first);
+       if (encoder.buffer_empty())
+           first = encoder.encode(first, last);
+       else
+           first = encoder.flush(first, last);
+       if (first == last)
+           break;
+       r_.len = encoder.output_begin() - r_.s;
+       if (!kills) {
+           kills = r_.s;
+           r_.s = 0;
+           grow(r_.len + last - first);
+           memcpy(r_.s, kills, r_.len);
+       } else
+           grow(r_.len + last - first);
+    }
+    if (kills)
+       delete[] reinterpret_cast<char*>(kills - memo_space);
+}
+
+template <typename T>
+inline void StringAccum::append_encoded(T &state,
+                                       const char *first,
+                                       const char *last) {
+    append_encoded(state,
+                  reinterpret_cast<const unsigned char *>(first),
+                  reinterpret_cast<const unsigned char *>(last));
+}
+
+template <typename T>
+inline void StringAccum::append_encoded(const char *first,
+                                       const char *last) {
+    append_encoded<T>(reinterpret_cast<const unsigned char *>(first),
+                     reinterpret_cast<const unsigned char *>(last));
+}
+
+template <typename T>
+void StringAccum::append_encoded(T &encoder)
+{
+    while (!encoder.buffer_empty()) {
+       encoder.set_output(r_.s + r_.len, r_.s + r_.cap, 0);
+       if (encoder.flush_clear()) {
+           r_.len = encoder.output_begin() - r_.s;
+           break;
+       }
+       grow(r_.len + 10);
+    }
+}
+
+template <typename T>
+inline void StringAccum::append_encoded(const unsigned char *first,
+                                       const unsigned char *last)
+{
+    T encoder;
+    append_encoded(encoder, first, last);
+    if (!encoder.buffer_empty())
+       append_encoded(encoder);
+}
+
+template <typename I>
+inline void StringAccum::append_join(const String &joiner, I first, I last) {
+    bool later = false;
+    while (first != last) {
+       if (later)
+           *this << joiner;
+       later = true;
+       *this << *first;
+       ++first;
+    }
+}
+
+template <typename T>
+inline void StringAccum::append_join(const String &joiner, const T &x) {
+    append_join(joiner, x.begin(), x.end());
+}
+
+/** @brief Assign this StringAccum to @a x. */
+inline StringAccum &StringAccum::operator=(const StringAccum &x) {
+    if (&x != this) {
+       if (out_of_memory())
+           r_.cap = 0;
+       r_.len = 0;
+       append(x.data(), x.length());
+    }
+    return *this;
+}
+
+#if HAVE_CXX_RVALUE_REFERENCES
+/** @brief Move-assign this StringAccum to @a x. */
+inline StringAccum &StringAccum::operator=(StringAccum &&x) {
+    x.swap(*this);
+    return *this;
+}
+#endif
+
+/** @relates StringAccum
+    @brief Append character @a c to StringAccum @a sa.
+    @return @a sa
+    @note Same as @a sa.append(@a c). */
+inline StringAccum &operator<<(StringAccum &sa, char c) {
+    sa.append(c);
+    return sa;
+}
+
+/** @relates StringAccum
+    @brief Append character @a c to StringAccum @a sa.
+    @return @a sa
+    @note Same as @a sa.append(@a c). */
+inline StringAccum &operator<<(StringAccum &sa, unsigned char c) {
+    sa.append(c);
+    return sa;
+}
+
+/** @relates StringAccum
+    @brief Append null-terminated C string @a cstr to StringAccum @a sa.
+    @return @a sa
+    @note Same as @a sa.append(@a cstr). */
+inline StringAccum &operator<<(StringAccum &sa, const char *cstr) {
+    sa.append(cstr);
+    return sa;
+}
+
+/** @relates StringAccum
+    @brief Append "true" or "false" to @a sa, depending on @a x.
+    @return @a sa */
+inline StringAccum &operator<<(StringAccum &sa, bool x) {
+    sa.append(String_generic::bool_data + (-x & 6), 5 - x);
+    return sa;
+}
+
+/** @relates StringAccum
+    @brief Append decimal representation of @a x to @a sa.
+    @return @a sa */
+inline StringAccum &operator<<(StringAccum &sa, short x) {
+    return sa << static_cast<long>(x);
+}
+
+/** @overload */
+inline StringAccum &operator<<(StringAccum &sa, unsigned short x) {
+    return sa << static_cast<unsigned long>(x);
+}
+
+/** @overload */
+inline StringAccum &operator<<(StringAccum &sa, int x) {
+    return sa << static_cast<long>(x);
+}
+
+/** @overload */
+inline StringAccum &operator<<(StringAccum &sa, unsigned x) {
+    return sa << static_cast<unsigned long>(x);
+}
+
+/** @relates StringAccum
+    @brief Append the contents of @a str to @a sa.
+    @return @a sa */
+template <typename T>
+inline StringAccum &operator<<(StringAccum &sa, const String_base<T> &str) {
+    sa.append(str.data(), str.length());
+    return sa;
+}
+
+inline StringAccum &operator<<(StringAccum &sa, const std::string &str) {
+    sa.append(str.data(), str.length());
+    return sa;
+}
+
+/** @relates StringAccum
+    @brief Append the contents of @a x to @a sa.
+    @return @a sa */
+inline StringAccum &operator<<(StringAccum &sa, const StringAccum &x) {
+    sa.append(x.begin(), x.end());
+    return sa;
+}
+
+inline bool operator==(StringAccum &sa, const char *cstr) {
+    return strcmp(sa.c_str(), cstr) == 0;
+}
+
+inline bool operator!=(StringAccum &sa, const char *cstr) {
+    return strcmp(sa.c_str(), cstr) != 0;
+}
+
+inline void swap(StringAccum& a, StringAccum& b) {
+    a.swap(b);
+}
+
+} // namespace lcdf
+#undef LCDF_SNPRINTF_ATTR
+#endif
diff --git a/silo/masstree/string.cc b/silo/masstree/string.cc
new file mode 100644 (file)
index 0000000..2ebe7e8
--- /dev/null
@@ -0,0 +1,1381 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+/*
+ * string.{cc,hh} -- a String class with shared substrings
+ * Eddie Kohler
+ *
+ * Copyright (c) 1999-2000 Massachusetts Institute of Technology
+ * Copyright (c) 2001-2013 Eddie Kohler
+ * Copyright (c) 2008-2009 Meraki, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Click LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Click LICENSE file; the license in that file
+ * is legally binding.
+ */
+
+#include "string.hh"
+#include "straccum.hh"
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <vector>
+namespace lcdf {
+
+/** @file string.hh
+ * @brief The LCDF String class.
+ */
+
+/** @class String
+ * @brief A string of characters.
+ *
+ * The String class represents a string of characters.  Strings may be
+ * constructed from C strings, characters, numbers, and so forth.  They may
+ * also be added together.  The underlying character arrays are dynamically
+ * allocated; String operations allocate and free memory as needed.  A String
+ * and its substrings generally share memory.  Accessing a character by index
+ * takes O(1) time; so does creating a substring.
+ *
+ * <h3>Out-of-memory strings</h3>
+ *
+ * When there is not enough memory to create a particular string, a special
+ * "out-of-memory" string is returned instead. Out-of-memory strings are
+ * contagious: the result of any concatenation operation involving an
+ * out-of-memory string is another out-of-memory string. Thus, the final
+ * result of a series of String operations will be an out-of-memory string,
+ * even if the out-of-memory condition occurs in the middle.
+ *
+ * The canonical out-of-memory string is 14 bytes long, and equals the UTF-8
+ * encoding of "\U0001F4A3ENOMEM\U0001F4A3" (that is, U+1F4A3 BOMB +
+ * "ENOMEM" + U+1F4A3 BOMB). This sequence is unlikely to show up in normal
+ * text, compares high relative to most other textual strings, and is valid
+ * UTF-8.
+ *
+ * All canonical out-of-memory strings are equal and share the same data(),
+ * which is different from the data() of any other string. See
+ * String::out_of_memory_data(). The String::make_out_of_memory() function
+ * returns a canonical out-of-memory string.
+ *
+ * Other strings may also be out-of-memory strings. For example,
+ * String::make_stable(String::out_of_memory_data()) ==
+ * String::make_out_of_memory(), and some (but not all) substrings of
+ * out-of-memory strings are also out-of-memory strings.
+ */
+
+const char String_generic::empty_data[] = "";
+// oom_data is the UTF-8 encoding of U+1F4A3 BOMB + "ENOMEM" + U+1F4A3 BOMB
+const char String_generic::out_of_memory_data[] = "\360\237\222\243ENOMEM\360\237\222\243";
+const char String_generic::bool_data[] = "false\0true";
+const char String_generic::base64_encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+const unsigned char String_generic::base64_decoding_map[] =
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\x3F\0\0\0\x40"
+        "\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\0\0\0\0\0\0"
+        "\0\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\0\0\0\0\0"
+        "\0\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29"
+        "\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+const char String::int_data[] = "0\0001\0002\0003\0004\0005\0006\0007\0008\0009";
+
+#if HAVE_STRING_PROFILING > 1
+# define MEMO_INITIALIZER_TAIL , 0, 0
+#else
+# define MEMO_INITIALIZER_TAIL
+#endif
+
+const String::rep_type String::null_string_rep = {
+    String_generic::empty_data, 0, 0
+};
+const String::rep_type String::oom_string_rep = {
+    String_generic::out_of_memory_data, String_generic::out_of_memory_length, 0
+};
+const String::rep_type String::zero_string_rep = {
+    &int_data[0], 0, 0
+};
+
+#if HAVE_STRING_PROFILING
+uint64_t String::live_memo_count;
+uint64_t String::memo_sizes[55];
+uint64_t String::live_memo_sizes[55];
+uint64_t String::live_memo_bytes[55];
+# if HAVE_STRING_PROFILING > 1
+String::memo_type *String::live_memos[55];
+# endif
+#endif
+
+int
+String_generic::compare(const char* a, int a_len, const char* b, int b_len)
+{
+    if (a != b) {
+        int len = a_len < b_len ? a_len : b_len;
+        int cmp = memcmp(a, b, len);
+        if (cmp != 0)
+            return cmp;
+    }
+    return a_len - b_len;
+}
+
+int String_generic::natural_compare(const char* a, int a_len,
+                                    const char* b, int b_len) {
+    const char* ae = a + a_len;
+    const char* be = b + b_len;
+    const char* aperiod = 0;
+    bool aperiod_negative = false;
+    int raw_compare = 0;
+
+    while (a < ae && b < be) {
+        if (isdigit((unsigned char) *a) && isdigit((unsigned char) *b)) {
+            // compare the two numbers, but treat them as strings
+            // (a decimal conversion might cause overflow)
+            bool potential_decimal = (a == aperiod);
+
+            // check if both are negative (note that if we get here, entire
+            // string prefixes are identical)
+            bool negative = false;
+            if (a > ae - a_len && a[-1] == '-'
+                && (a == ae - a_len + 1
+                    || isspace((unsigned char) a[-2])))
+                negative = true;
+
+            // skip initial '0's, but remember any difference in length
+            const char *ia = a, *ib = b;
+            while (a < ae && *a == '0')
+                ++a;
+            while (b < be && *b == '0')
+                ++b;
+            int longer_zeros = (a - ia) - (b - ib);
+
+            // walk over digits, remembering first nonidentical digit comparison
+            int digit_compare = 0;
+            bool a_good, b_good;
+            while (1) {
+                a_good = a < ae && isdigit((unsigned char) *a);
+                b_good = b < be && isdigit((unsigned char) *b);
+                if (!a_good || !b_good)
+                    break;
+                if (digit_compare == 0)
+                    digit_compare = *a - *b;
+                ++a;
+                ++b;
+            }
+
+            // real number comparison: leading zeros are significant,
+            // digit comparisons take precedence
+            if (potential_decimal) {
+                const char *ax = a, *bx = b;
+                while (ax < ae && isdigit((unsigned char) *ax))
+                    ++ax;
+                while (bx < be && isdigit((unsigned char) *bx))
+                    ++bx;
+                // watch for IP addresses: don't treat "0.2." like a decimal
+                if (!(ax + 1 < ae && *ax == '.' && !isspace((unsigned char) ax[1]))
+                    && !(bx + 1 < be && *bx == '.' && !isspace((unsigned char) bx[1]))) {
+                    negative = aperiod_negative;
+                    if (longer_zeros)
+                        return negative ? 1 : -1;
+                    if (digit_compare)
+                        a_good = b_good;
+                }
+            }
+            // if one number is longer, it must also be larger
+            if (a_good != b_good)
+                return negative == a_good ? -1 : 1;
+            // otherwise, digit comparisons take precedence
+            if (digit_compare)
+                return negative == (digit_compare > 0) ? -1 : 1;
+            // as a last resort, the longer string of zeros is greater
+            if (longer_zeros)
+                return longer_zeros;
+            // prepare for potential decimal comparison later
+            if (!aperiod) {
+                a_good = a + 1 < ae && *a == '.'
+                    && isdigit((unsigned char) a[1]);
+                b_good = b + 1 < be && *b == '.'
+                    && isdigit((unsigned char) b[1]);
+                if (a_good != b_good)
+                    return negative == b_good ? 1 : -1;
+                else if (a_good) {
+                    aperiod = a + 1;
+                    aperiod_negative = negative;
+                }
+            }
+
+            // if we get here, the numeric portions were byte-for-byte
+            // identical; move on
+        } else if (isdigit((unsigned char) *a))
+            return isalpha((unsigned char) *b) ? -1 : 1;
+        else if (isdigit((unsigned char) *b))
+            return isalpha((unsigned char) *a) ? 1 : -1;
+        else {
+            int alower = (unsigned char) tolower((unsigned char) *a);
+            int blower = (unsigned char) tolower((unsigned char) *b);
+            if (alower != blower)
+                return alower - blower;
+            if (raw_compare == 0)
+                raw_compare = (unsigned char) *a - (unsigned char) *b;
+            if (*a != '.')
+                aperiod = 0;
+            ++a;
+            ++b;
+        }
+    }
+
+    if ((ae - a) != (be - b))
+        return (ae - a) - (be - b);
+    else
+        return raw_compare;
+}
+
+hashcode_t
+String_generic::hashcode(const char *s, int len)
+{
+    if (len <= 0)
+        return 0;
+
+    uint32_t hash = len;
+    int rem = hash & 3;
+    const char *end = s + len - rem;
+    uint32_t last16;
+
+#if !HAVE_INDIFFERENT_ALIGNMENT
+    if (!(reinterpret_cast<uintptr_t>(s) & 1)) {
+#endif
+#define get16(p) (*reinterpret_cast<const uint16_t *>((p)))
+        for (; s != end; s += 4) {
+            hash += get16(s);
+            uint32_t tmp = (get16(s + 2) << 11) ^ hash;
+            hash = (hash << 16) ^ tmp;
+            hash += hash >> 11;
+        }
+        if (rem >= 2) {
+            last16 = get16(s);
+            goto rem2;
+        }
+#undef get16
+#if !HAVE_INDIFFERENT_ALIGNMENT
+    } else {
+# if !__i386__ && !__x86_64__ && !__arch_um__
+#  define get16(p) (((unsigned char) (p)[0] << 8) + (unsigned char) (p)[1])
+# else
+#  define get16(p) ((unsigned char) (p)[0] + ((unsigned char) (p)[1] << 8))
+# endif
+        // should be exactly the same as the code above
+        for (; s != end; s += 4) {
+            hash += get16(s);
+            uint32_t tmp = (get16(s + 2) << 11) ^ hash;
+            hash = (hash << 16) ^ tmp;
+            hash += hash >> 11;
+        }
+        if (rem >= 2) {
+            last16 = get16(s);
+            goto rem2;
+        }
+# undef get16
+    }
+#endif
+
+    /* Handle end cases */
+    if (0) {                    // weird organization avoids uninitialized
+      rem2:                     // variable warnings
+        if (rem == 3) {
+            hash += last16;
+            hash ^= hash << 16;
+            hash ^= ((unsigned char) s[2]) << 18;
+            hash += hash >> 11;
+        } else {
+            hash += last16;
+            hash ^= hash << 11;
+            hash += hash >> 17;
+        }
+    } else if (rem == 1) {
+        hash += (unsigned char) *s;
+        hash ^= hash << 10;
+        hash += hash >> 1;
+    }
+
+    /* Force "avalanching" of final 127 bits */
+    hash ^= hash << 3;
+    hash += hash >> 5;
+    hash ^= hash << 4;
+    hash += hash >> 17;
+    hash ^= hash << 25;
+    hash += hash >> 6;
+
+    return hash;
+}
+
+int
+String_generic::find_left(const char *s, int len, int start,
+                          char x)
+{
+    if (start < 0)
+        start = 0;
+    for (int i = start; i < len; ++i)
+        if (s[i] == x)
+            return i;
+    return -1;
+}
+
+int
+String_generic::find_left(const char *s, int len, int start,
+                          const char *x, int x_len)
+{
+    if (start < 0)
+        start = 0;
+    if (x_len == 0)
+        return start <= len ? start : -1;
+    int max_pos = len - x_len;
+    for (int i = start; i <= max_pos; ++i)
+        if (memcmp(s + i, x, x_len) == 0)
+            return i;
+    return -1;
+}
+
+int
+String_generic::find_right(const char *s, int len, int start,
+                           char x)
+{
+    if (start >= len)
+        start = len - 1;
+    for (int i = start; i >= 0; --i)
+        if (s[i] == x)
+            return i;
+    return -1;
+}
+
+int
+String_generic::find_right(const char *s, int len, int start,
+                           const char *x, int x_len)
+{
+    if (start >= len)
+        start = len - x_len;
+    if (x_len == 0)
+        return start >= 0 ? start : -1;
+    for (int i = start; i >= 0; --i)
+        if (memcmp(s + i, x, x_len) == 0)
+            return i;
+    return -1;
+}
+
+long String_generic::to_i(const char* s, const char* ends) {
+    bool neg;
+    if (s != ends && (s[0] == '-' || s[0] == '+')) {
+        neg = s[0] == '-';
+        ++s;
+    } else
+        neg = false;
+    if (s == ends || !isdigit((unsigned char) *s))
+        return 0;
+    unsigned long x = (unsigned char) *s - '0';
+    for (++s; s != ends && isdigit((unsigned char) *s); ++s)
+        x = x * 10 + *s - '0';  // XXX overflow
+    return neg ? -x : x;
+}
+
+bool String_generic::glob_match(const char* sbegin, int slen,
+                                const char* pbegin, int plen) {
+    const char* send = sbegin + slen;
+    const char* pend = pbegin + plen;
+
+    // quick common-case check for suffix matches
+    while (pbegin < pend && sbegin < send
+           && pend[-1] != '*' && pend[-1] != '?' && pend[-1] != ']'
+           && (pbegin + 1 == pend || pend[-2] != '\\'))
+        if (pend[-1] == send[-1])
+            --pend, --send;
+        else
+            return false;
+
+    std::vector<const char*> state, nextstate;
+    state.push_back(pbegin);
+
+    for (const char* s = sbegin; s != send && state.size(); ++s) {
+        nextstate.clear();
+        for (const char** pp = state.data(); pp != state.data() + state.size(); ++pp)
+            if (*pp != pend) {
+              reswitch:
+                switch (**pp) {
+                  case '?':
+                    nextstate.push_back(*pp + 1);
+                    break;
+                  case '*':
+                    if (*pp + 1 == pend)
+                        return true;
+                    if (nextstate.empty() || nextstate.back() != *pp)
+                        nextstate.push_back(*pp);
+                    ++*pp;
+                    goto reswitch;
+                  case '\\':
+                    if (*pp + 1 != pend)
+                        ++*pp;
+                    goto normal_char;
+                  case '[': {
+                      const char *ec = *pp + 1;
+                      bool negated;
+                      if (ec != pend && *ec == '^') {
+                          negated = true;
+                          ++ec;
+                      } else
+                          negated = false;
+                      if (ec == pend)
+                          goto normal_char;
+
+                      bool found = false;
+                      do {
+                          if (*++ec == *s)
+                              found = true;
+                      } while (ec != pend && *ec != ']');
+                      if (ec == pend)
+                          goto normal_char;
+
+                      if (found == !negated)
+                          nextstate.push_back(ec + 1);
+                      break;
+                  }
+                  normal_char:
+                  default:
+                    if (**pp == *s)
+                        nextstate.push_back(*pp + 1);
+                    break;
+                }
+            }
+        state.swap(nextstate);
+    }
+
+    for (const char** pp = state.data(); pp != state.data() + state.size(); ++pp) {
+        while (*pp != pend && **pp == '*')
+            ++*pp;
+        if (*pp == pend)
+            return true;
+    }
+    return false;
+}
+
+
+/** @cond never */
+#if HAVE_STRING_PROFILING
+void String::memo_type::account_new() {
+    int bucket = profile_memo_size_bucket(this->dirty, this->capacity);
+    ++memo_sizes[bucket];
+    ++live_memo_sizes[bucket];
+    live_memo_bytes[bucket] += this->capacity;
+    ++live_memo_count;
+# if HAVE_STRING_PROFILING > 1
+    this->pprev = &live_memos[bucket];
+    if ((this->next = *this->pprev))
+        this->next->pprev = &this->next;
+    *this->pprev = memo;
+# endif
+}
+
+void String::memo_type::account_destroy() {
+    int bucket = profile_memo_size_bucket(this->dirty, this->capacity);
+    --live_memo_sizes[bucket];
+    live_memo_bytes[bucket] -= this->capacity;
+    --live_memo_count;
+# if HAVE_STRING_PROFILING > 1
+    if ((*this->pprev = this->next))
+        this->next->pprev = this->pprev;
+# endif
+}
+#endif
+
+inline String::memo_type* String::create_memo(int capacity, int dirty) {
+    assert(capacity > 0 && capacity >= dirty);
+    memo_type *memo =
+        reinterpret_cast<memo_type *>(new char[capacity + MEMO_SPACE]);
+    if (memo)
+        memo->initialize(capacity, dirty);
+    return memo;
+}
+
+void
+String::delete_memo(memo_type *memo)
+{
+    assert(memo->capacity > 0);
+    assert(memo->capacity >= memo->dirty);
+    memo->account_destroy();
+    delete[] reinterpret_cast<char*>(memo);
+}
+
+
+#if HAVE_STRING_PROFILING
+void
+String::one_profile_report(StringAccum &sa, int i, int examples)
+{
+    if (i <= 16)
+        sa << "memo_dirty_" << i;
+    else if (i < 25) {
+        uint32_t s = (i - 17) * 2 + 17;
+        sa << "memo_cap_" << s << '_' << (s + 1);
+    } else if (i < 29) {
+        uint32_t s = (i - 25) * 8 + 33;
+        sa << "memo_cap_" << s << '_' << (s + 7);
+    } else {
+        uint32_t s1 = (1U << (i - 23)) + 1;
+        uint32_t s2 = (s1 - 1) << 1;
+        sa << "memo_cap_" << s1 << '_' << s2;
+    }
+    sa << '\t' << live_memo_sizes[i] << '\t' << memo_sizes[i] << '\t' << live_memo_bytes[i] << '\n';
+    if (examples) {
+# if HAVE_STRING_PROFILING > 1
+        for (memo_type *m = live_memos[i]; m; m = m->next) {
+            sa << "    [" << m->dirty << "] ";
+            uint32_t dirty = m->dirty;
+            if (dirty > 0 && m->real_data[dirty - 1] == '\0')
+                --dirty;
+            sa.append(m->real_data, dirty > 128 ? 128 : dirty);
+            sa << '\n';
+        }
+# endif
+    }
+}
+
+void
+String::profile_report(StringAccum &sa, int examples)
+{
+    uint64_t all_live_sizes = 0, all_sizes = 0, all_live_bytes = 0;
+    for (int i = 0; i < 55; ++i) {
+        if (memo_sizes[i])
+            one_profile_report(sa, i, examples);
+        all_live_sizes += live_memo_sizes[i];
+        all_sizes += memo_sizes[i];
+        all_live_bytes += live_memo_bytes[i];
+    }
+    sa << "memo_total\t" << all_live_sizes << '\t' << all_sizes << '\t' << all_live_bytes << '\n';
+}
+#endif
+
+/** @endcond never */
+
+
+/** @brief Construct a base-10 string representation of @a x. */
+String::String(int x)
+{
+    if (x >= 0 && x < 10)
+        _r.assign(int_data + 2 * x, 1, 0);
+    else {
+        char buf[128];
+        sprintf(buf, "%d", x);
+        assign(buf, -1, false);
+    }
+}
+
+/** @overload */
+String::String(unsigned x)
+{
+    if (x < 10)
+        _r.assign(int_data + 2 * x, 1, 0);
+    else {
+        char buf[128];
+        sprintf(buf, "%u", x);
+        assign(buf, -1, false);
+    }
+}
+
+/** @overload */
+String::String(long x)
+{
+    if (x >= 0 && x < 10)
+        _r.assign(int_data + 2 * x, 1, 0);
+    else {
+        char buf[128];
+        sprintf(buf, "%ld", x);
+        assign(buf, -1, false);
+    }
+}
+
+/** @overload */
+String::String(unsigned long x)
+{
+    if (x < 10)
+        _r.assign(int_data + 2 * x, 1, 0);
+    else {
+        char buf[128];
+        sprintf(buf, "%lu", x);
+        assign(buf, -1, false);
+    }
+}
+
+/** @overload */
+String::String(long long x)
+{
+    if (x >= 0 && x < 10)
+        _r.assign(int_data + 2 * x, 1, 0);
+    else {
+        char buf[128];
+        sprintf(buf, "%lld", x);
+        assign(buf, -1, false);
+    }
+}
+
+/** @overload */
+String::String(unsigned long long x)
+{
+    if (x < 10)
+        _r.assign(int_data + 2 * x, 1, 0);
+    else {
+        char buf[128];
+        sprintf(buf, "%llu", x);
+        assign(buf, -1, false);
+    }
+}
+
+String::String(double x)
+{
+    char buf[128];
+    int len = sprintf(buf, "%.12g", x);
+    assign(buf, len, false);
+}
+
+String
+String::hard_make_stable(const char *s, int len)
+{
+    if (len < 0)
+        len = strlen(s);
+    return String(s, len, 0);
+}
+
+String
+String::make_fill(int c, int len)
+{
+    String s;
+    s.append_fill(c, len);
+    return s;
+}
+
+void
+String::assign_out_of_memory()
+{
+    _r.deref();
+    _r = oom_string_rep;
+}
+
+void
+String::assign(const char *s, int len, bool need_deref)
+{
+    if (!s) {
+        assert(len <= 0);
+        len = 0;
+    } else if (len < 0)
+        len = strlen(s);
+
+    // need to start with dereference
+    memo_type* m;
+    if (need_deref) {
+        if (unlikely((m = _r.memo())
+                     && s >= m->real_data
+                     && s + len <= m->real_data + m->capacity)) {
+            // Be careful about "String s = ...; s = s.c_str();"
+            _r.assign_noref(s, len, m);
+            return;
+        } else
+            deref();
+    }
+
+    if (len == 0) {
+        m = 0;
+        s = String_generic::empty_data;
+
+    } else {
+        // Make the memo a multiple of 16 characters and bigger than 'len'.
+        int memo_capacity = (len + 15 + MEMO_SPACE) & ~15;
+        m = create_memo(memo_capacity - MEMO_SPACE, len);
+        if (!m) {
+            _r.reset_ref();
+            assign_out_of_memory();
+            return;
+        }
+        memcpy(m->real_data, s, len);
+        s = m->real_data;
+    }
+
+    _r.assign_noref(s, len, m);
+}
+
+/** @brief Append @a len unknown characters to this string.
+ * @return Modifiable pointer to the appended characters.
+ *
+ * The caller may safely modify the returned memory. Null is returned if
+ * the string becomes out-of-memory. */
+char *
+String::append_uninitialized(int len)
+{
+    // Appending anything to "out of memory" leaves it as "out of memory"
+    if (unlikely(len <= 0) || out_of_memory())
+        return 0;
+
+    // If we can, append into unused space. First, we check that there's
+    // enough unused space for 'len' characters to fit; then, we check
+    // that the unused space immediately follows the data in '*this'.
+    uint32_t dirty;
+    memo_type* m = _r.memo();
+    if (m && ((dirty = m->dirty), m->capacity > dirty + len)) {
+        char *real_dirty = m->real_data + dirty;
+        if (real_dirty == _r.data + _r.length) {
+            m->dirty = dirty + len;
+            _r.length += len;
+            assert(m->dirty < m->capacity);
+#if HAVE_STRING_PROFILING
+            profile_update_memo_dirty(m, dirty, dirty + len, m->capacity);
+#endif
+            return real_dirty;
+        }
+    }
+
+    // Now we have to make new space. Make sure the memo is a multiple of 16
+    // bytes and that it is at least 16. But for large strings, allocate a
+    // power of 2, since power-of-2 sizes minimize waste in frequently-used
+    // allocators, like Linux kmalloc.
+    int want_memo_len = _r.length + len + MEMO_SPACE;
+    int memo_capacity;
+    if (want_memo_len <= 1024)
+        memo_capacity = (want_memo_len + 15) & ~15;
+    else
+        for (memo_capacity = 2048; memo_capacity < want_memo_len; )
+            memo_capacity *= 2;
+
+    m = create_memo(memo_capacity - MEMO_SPACE, _r.length + len);
+    if (!m) {
+        assign_out_of_memory();
+        return 0;
+    }
+
+    char *new_data = m->real_data;
+    memcpy(new_data, _r.data, _r.length);
+
+    deref();
+    _r.assign_noref(new_data, _r.length + len, m);
+    return const_cast<char*>(_r.data + _r.length - len);
+}
+
+void
+String::append(const char* s, int len, memo_type* memo)
+{
+    if (!s) {
+        assert(len <= 0);
+        len = 0;
+    } else if (len < 0)
+        len = strlen(s);
+
+    memo_type* my_memo;
+    if (unlikely(len == 0) || out_of_memory())
+        /* do nothing */;
+    else if (unlikely(s == String_generic::out_of_memory_data) && !memo)
+        // Appending "out of memory" to a regular string makes it "out of
+        // memory"
+        assign_out_of_memory();
+    else if (_r.length == 0 && reinterpret_cast<uintptr_t>(memo) > 1) {
+        deref();
+        _r.assign(s, len, memo);
+    } else if (likely(!((my_memo = _r.memo())
+                        && s >= my_memo->real_data
+                        && s + len <= my_memo->real_data + my_memo->capacity))) {
+        if (char *space = append_uninitialized(len))
+            memcpy(space, s, len);
+    } else {
+        String preserve_s(*this);
+        if (char *space = append_uninitialized(len))
+            memcpy(space, s, len);
+    }
+}
+
+/** @brief Append @a len copies of character @a c to this string. */
+void
+String::append_fill(int c, int len)
+{
+    assert(len >= 0);
+    if (char *space = append_uninitialized(len))
+        memset(space, c, len);
+}
+
+/** @brief Ensure the string's data is unshared and return a mutable
+    pointer to it. */
+char *
+String::mutable_data()
+{
+    // If _memo has a capacity (it's not one of the special strings) and it's
+    // uniquely referenced, return _data right away.
+    if (!is_shared())
+        return const_cast<char *>(_r.data);
+
+    // Otherwise, make a copy of it. Rely on: deref() doesn't change _data or
+    // _length; and if _capacity == 0, then deref() doesn't free _real_data.
+    // But in multithreaded situations we must hold a local copy of memo!
+    String do_not_delete_underlying_memo(*this);
+    deref();
+    assign(_r.data, _r.length, false);
+    return const_cast<char *>(_r.data);
+}
+
+const char *
+String::hard_c_str() const
+{
+    // See also c_str().
+    // We may already have a '\0' in the right place.  If _memo has no
+    // capacity, then this is one of the special strings (null or
+    // stable). We are guaranteed, in these strings, that _data[_length]
+    // exists. Otherwise must check that _data[_length] exists.
+    const char *end_data = _r.data + _r.length;
+    memo_type* m = _r.memo();
+    if ((m && end_data >= m->real_data + m->dirty)
+        || *end_data != '\0') {
+        if (char *x = const_cast<String *>(this)->append_uninitialized(1)) {
+            *x = '\0';
+            --_r.length;
+        }
+    }
+    return _r.data;
+}
+
+/** @brief Null-terminate the string and return a mutable pointer to its
+    data.
+    @sa String::c_str */
+char *
+String::mutable_c_str()
+{
+    (void) mutable_data();
+    (void) c_str();
+    return const_cast<char *>(_r.data);
+}
+
+/** @brief Return a substring of this string, consisting of the @a len
+    characters starting at index @a pos.
+    @param pos substring's first position relative to the string
+    @param len length of substring
+
+    If @a pos is negative, starts that far from the end of the string. If @a
+    len is negative, leaves that many characters off the end of the string.
+    If @a pos and @a len specify a substring that is partly outside the
+    string, only the part within the string is returned. If the substring is
+    beyond either end of the string, returns an empty string (but this
+    should be considered a programming error; a future version may generate
+    a warning for this case).
+
+    @note String::substr() is intended to behave like Perl's substr(). */
+String
+String::substr(int pos, int len) const
+{
+    if (pos < 0)
+        pos += _r.length;
+
+    int pos2;
+    if (len < 0)
+        pos2 = _r.length + len;
+    else if (pos >= 0 && len >= _r.length) // avoid integer overflow
+        pos2 = _r.length;
+    else
+        pos2 = pos + len;
+
+    if (pos < 0)
+        pos = 0;
+    if (pos2 > _r.length)
+        pos2 = _r.length;
+
+    if (pos >= pos2)
+        return String();
+    else {
+        _r.ref();
+        return String(_r.data + pos, pos2 - pos, _r.memo());
+    }
+}
+
+static String
+hard_lower(const String &s, int pos)
+{
+    String new_s(s.data(), s.length());
+    char *x = const_cast<char *>(new_s.data()); // know it's mutable
+    int len = s.length();
+    for (; pos < len; pos++)
+        x[pos] = tolower((unsigned char) x[pos]);
+    return new_s;
+}
+
+/** @brief Return a lowercased version of this string.
+
+    Translates the ASCII characters 'A' through 'Z' into their lowercase
+    equivalents. */
+String
+String::lower() const
+{
+    // avoid copies
+    if (!out_of_memory())
+        for (int i = 0; i < _r.length; i++)
+            if (_r.data[i] >= 'A' && _r.data[i] <= 'Z')
+                return hard_lower(*this, i);
+    return *this;
+}
+
+static String
+hard_upper(const String &s, int pos)
+{
+    String new_s(s.data(), s.length());
+    char *x = const_cast<char *>(new_s.data()); // know it's mutable
+    int len = s.length();
+    for (; pos < len; pos++)
+        x[pos] = toupper((unsigned char) x[pos]);
+    return new_s;
+}
+
+/** @brief Return an uppercased version of this string.
+
+    Translates the ASCII characters 'a' through 'z' into their uppercase
+    equivalents. */
+String
+String::upper() const
+{
+    // avoid copies
+    for (int i = 0; i < _r.length; i++)
+        if (_r.data[i] >= 'a' && _r.data[i] <= 'z')
+            return hard_upper(*this, i);
+    return *this;
+}
+
+static String
+hard_printable(const String &s, int pos, int type)
+{
+    StringAccum sa(s.length() * 2);
+    sa.append(s.data(), pos);
+    const unsigned char *x = reinterpret_cast<const unsigned char *>(s.data());
+    int len = s.length();
+    for (; pos < len; pos++) {
+        if (type == 2 && (x[pos] == '\\' || x[pos] == '\"'))
+            sa << '\\' << x[pos];
+        else if (x[pos] >= 32 && x[pos] < 127)
+            sa << x[pos];
+        else if (x[pos] < 32 && type == 2) {
+            if (x[pos] >= 9 && x[pos] <= 13)
+                sa << '\\' << ("tnvfr"[x[pos] - 9]);
+            else if (char *buf = sa.extend(4, 1))
+                sprintf(buf, "\\%03o", x[pos]);
+        } else if (x[pos] < 32 && type != 1)
+            sa << '^' << (unsigned char)(x[pos] + 64);
+        else if (char *buf = sa.extend(4, 1))
+            sprintf(buf, "\\%03o", x[pos]);
+    }
+    return sa.take_string();
+}
+
+/** @brief Return a "printable" version of this string.
+    @param type quoting type
+
+    The default quoting type (0) translates control characters 0-31 into
+    "control" sequences, such as "^@" for the null character, and characters
+    127-255 into octal escape sequences, such as "\377" for 255. Quoting
+    type 1 translates all characters outside of 32-126 into octal escape
+    sequences. Quoting type 2 uses C escapes, including "\\" and "\"". */
+String
+String::printable(int type) const
+{
+    // avoid copies
+    if (!out_of_memory())
+        for (int i = 0; i < _r.length; i++)
+            if (_r.data[i] < 32 || _r.data[i] > 126)
+                return hard_printable(*this, i, type);
+    return *this;
+}
+
+String String::to_hex() const {
+    StringAccum sa;
+    static const char hexval[] = "0123456789ABCDEF";
+    char* x = sa.extend(2 * _r.length);
+    for (int i = 0; i != _r.length; ++i) {
+        *x++ = hexval[(unsigned char) _r.data[i] >> 4];
+        *x++ = hexval[_r.data[i] & 15];
+    }
+    return sa.take_string();
+}
+
+/** @brief Return the substring with left whitespace removed. */
+String
+String::ltrim() const
+{
+    return String_generic::ltrim(*this);
+}
+
+/** @brief Return the substring with right whitespace removed. */
+String
+String::rtrim() const
+{
+    return String_generic::rtrim(*this);
+}
+
+/** @brief Return the substring with left and right whitespace removed. */
+String
+String::trim() const
+{
+    return String_generic::trim(*this);
+}
+
+void
+String::align(int n)
+{
+    int offset = reinterpret_cast<uintptr_t>(_r.data) % n;
+    if (offset) {
+        String s;
+        s.append_uninitialized(_r.length + n + 1);
+        offset = reinterpret_cast<uintptr_t>(s._r.data) % n;
+        memcpy((char *)s._r.data + n - offset, _r.data, _r.length);
+        s._r.data += n - offset;
+        s._r.length = _r.length;
+        *this = s;
+    }
+}
+
+/** @brief Return the pointer to the next character in UTF-8 encoding. */
+const unsigned char *
+String::skip_utf8_char(const unsigned char *s, const unsigned char *end)
+{
+    int c = *s;
+    if (c > 0 && c < 0x80)
+        return s + 1;
+    else if (c < 0xC2)
+        /* zero, or bad/overlong encoding */;
+    else if (c < 0xE0) {        // 2 bytes: U+80-U+7FF
+        if (likely(s + 1 < end
+                   && s[1] >= 0x80 && s[1] < 0xC0))
+            return s + 2;
+    } else if (c < 0xF0) {      // 3 bytes: U+800-U+FFFF
+        if (likely(s + 2 < end
+                   && s[1] >= 0x80 && s[1] < 0xC0
+                   && s[2] >= 0x80 && s[2] < 0xC0
+                   && (c != 0xE0 || s[1] >= 0xA0) /* not overlong encoding */
+                   && (c != 0xED || s[1] < 0xA0) /* not surrogate */))
+            return s + 3;
+    } else if (c < 0xF5) {      // 4 bytes: U+10000-U+10FFFF
+        if (likely(s + 3 < end
+                   && s[1] >= 0x80 && s[1] < 0xC0
+                   && s[2] >= 0x80 && s[2] < 0xC0
+                   && s[3] >= 0x80 && s[3] < 0xC0
+                   && (c != 0xF0 || s[1] >= 0x90) /* not overlong encoding */
+                   && (c != 0xF4 || s[1] < 0x90) /* not >U+10FFFF */))
+            return s + 4;
+    }
+    return s;
+}
+
+int
+String::parse_cesu8_char(const unsigned char *s, const unsigned char *end)
+{
+    if (s + 5 < end
+        && s[0] == 0xED
+        && s[1] >= 0xA0 && s[1] < 0xB0
+        && s[2] >= 0x80 && s[2] < 0xC0
+        && s[3] == 0xED
+        && s[4] >= 0xB0 && s[4] < 0xC0
+        && s[5] >= 0x80 && s[5] < 0xC0)
+        return 0x10000
+            + ((s[1] & 0x0F) << 16) + ((s[2] & 0x3F) << 10)
+            + ((s[4] & 0x0F) << 6) + (s[5] & 0x3F);
+    else
+        return 0;
+}
+
+static const uint16_t windows1252_c1_mapping[] = {
+    0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x20C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F,
+    0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x20DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178
+};
+
+String
+String::windows1252_to_utf8() const
+{
+    const unsigned char *s = ubegin(), *last = s, *ends = uend();
+    StringAccum sa;
+    for (; s != ends; ++s) {
+        int c = *s;
+        if (unlikely(c == 0)) {
+            sa.append(last, s);
+            last = s + 1;
+        } else if (likely(c < 128))
+            /* do nothing */;
+        else {
+            sa.append(last, s);
+            if (unlikely(c < 160))
+                c = windows1252_c1_mapping[c - 128];
+            sa.append_utf8(c);
+            last = s + 1;
+        }
+    }
+    if (last == ubegin())
+        return *this;
+    else {
+        sa.append(last, s);
+        return sa.take_string();
+    }
+}
+
+String
+String::utf16be_to_utf8(int flags) const
+{
+    const unsigned char *s = ubegin(), *ends = uend();
+    if ((ends - s) & 1)
+        --ends;
+    if ((flags & utf_strip_bom) && s != ends
+        && s[0] == 0xFE && s[1] == 0xFF)
+        s += 2;
+    StringAccum sa;
+    sa.reserve((ends - s) / 2);
+    for (; s != ends; s += 2) {
+        int c = s[0] * 256 + s[1];
+        if (likely(c < 0xD800) || c >= 0xE000)
+            sa.append_utf8(c);
+        else if (c < 0xDC00 && s + 2 != ends
+                 && s[2] >= 0xDC && s[2] < 0xE0) {
+            c = 0x10000 + ((c - 0xD800) << 10)
+                + ((s[2] & 0x03) << 8) + s[3];
+            sa.append_utf8(c);
+            s += 2;
+        } else if (c && (flags & utf_replacement))
+            sa.append_utf8(u_replacement);
+    }
+    return sa.take_string();
+}
+
+String
+String::utf16le_to_utf8(int flags) const
+{
+    const unsigned char *s = ubegin(), *ends = uend();
+    if ((ends - s) & 1)
+        --ends;
+    if ((flags & utf_strip_bom) && s != ends
+        && s[1] == 0xFE && s[0] == 0xFF)
+        s += 2;
+    StringAccum sa;
+    sa.reserve((ends - s) / 2);
+    for (; s != ends; s += 2) {
+        int c = s[1] * 256 + s[0];
+        if (likely(c < 0xD800) || c >= 0xE000)
+            sa.append_utf8(c);
+        else if (c < 0xDC00 && s + 2 != ends
+                 && s[3] >= 0xDC && s[3] < 0xE0) {
+            c = 0x10000 + ((c - 0xD800) << 10)
+                + ((s[3] & 0x03) << 8) + s[2];
+            sa.append_utf8(c);
+            s += 2;
+        } else if (c && (flags & utf_replacement))
+            sa.append_utf8(u_replacement);
+    }
+    return sa.take_string();
+}
+
+String
+String::utf16_to_utf8(int flags) const
+{
+    if (length() < 2)
+        return String();
+    const unsigned char *s = ubegin(), *ends = uend();
+    if (ends - s < 2)
+        return String();
+    int c = s[0] * 256 + s[1];
+    if (c == 0xFEFF || (c != 0xFFFE && !(flags & utf_prefer_le)))
+        return utf16be_to_utf8(flags);
+    else
+        return utf16le_to_utf8(flags);
+}
+
+String
+String::cesu8_to_utf8(int flags) const
+{
+    const unsigned char *s = ubegin(), *last = s, *t, *ends = uend();
+    StringAccum sa;
+    if (flags & utf_strip_bom)
+        s = last = skip_utf8_bom(s, ends);
+    while (s != ends) {
+        int c = s[0];
+        if (likely(c > 0 && c < 0x7F))
+            ++s;
+        else if (likely((t = skip_utf8_char(s, ends)) != s))
+            s = t;
+        else if ((c = parse_cesu8_char(s, ends))) {
+            sa.append(last, s);
+            sa.append_utf8(c);
+            s += 6;
+            last = s;
+        } else {
+            sa.append(last, s);
+            if ((flags & utf_replacement) && c != 0)
+                sa.append_utf8(u_replacement);
+            for (++s; s != ends && *s >= 0x80 && *s < 0xC0; ++s)
+                /* do nothing */;
+            last = s;
+        }
+    }
+    if (last == ubegin())
+        return *this;
+    else {
+        sa.append(last, s);
+        return sa.take_string();
+    }
+}
+
+String
+String::utf8_to_utf8(int flags) const
+{
+    const unsigned char *s = ubegin(), *last = s, *t, *ends = uend();
+    StringAccum sa;
+    if (flags & utf_strip_bom)
+        s = last = skip_utf8_bom(s, ends);
+    while (s != ends) {
+        int c = s[0];
+        if (likely(c > 0 && c < 0x7F))
+            ++s;
+        else if (likely((t = skip_utf8_char(s, ends)) != s))
+            s = t;
+        else {
+            sa.append(last, s);
+            if ((flags & utf_replacement) && c != 0)
+                sa.append_utf8(u_replacement);
+            for (++s; s != ends && *s >= 0x80 && *s < 0xC0; ++s)
+                /* do nothing */;
+            last = s;
+        }
+    }
+    if (last == ubegin())
+        return *this;
+    else {
+        sa.append(last, s);
+        return sa.take_string();
+    }
+}
+
+/** @brief Return a version of the string converted to UTF-8.
+
+    Null characters are dropped.  Then, if the result contains invalid
+    UTF-8, the string is assumed to be Windows-1252 encoded, and is
+    converted to UTF-8 accordingly. */
+String
+String::to_utf8(int flags) const
+{
+    const unsigned char *s = ubegin(), *ends = uend();
+    if (ends - s > 2) {
+        if ((s[0] == 0xFF && s[1] == 0xFE)
+            || (s[0] > 0 && s[0] < 0x80 && s[1] == 0))
+            return utf16le_to_utf8(flags);
+        else if ((s[0] == 0xFE && s[1] == 0xFF)
+                 || (s[0] == 0 && s[1] > 0 && s[1] < 0x80))
+            return utf16be_to_utf8(flags);
+    }
+
+    const unsigned char *last = s, *t;
+    if (ends - s > 3 && (flags & utf_strip_bom))
+        s = last = skip_utf8_bom(s, ends);
+    StringAccum sa;
+    bool any_long_utf8 = false;
+    int c;
+    while (s != ends) {
+        if (likely(*s > 0 && *s < 128))
+            ++s;
+        else if (likely((t = skip_utf8_char(s, ends)) != s)) {
+            any_long_utf8 = true;
+            s = t;
+        } else if ((c = parse_cesu8_char(s, ends)) != 0) {
+            any_long_utf8 = true;
+            sa.append(last, s);
+            sa.append_utf8(c);
+            s += 6;
+            last = s;
+        } else if (*s == 0) {
+            sa.append(last, s);
+            ++s;
+            last = s;
+        } else if (!any_long_utf8)
+            goto windows1252;
+        else {
+            sa.append(last, s);
+            int c = *s;
+            if (c < 160)
+                c = windows1252_c1_mapping[c - 128];
+            sa.append_utf8(c);
+            ++s;
+            last = s;
+        }
+    }
+
+ exit:
+    if (last == ubegin())
+        return *this;
+    else {
+        sa.append(last, ends);
+        return sa.take_string();
+    }
+
+ windows1252:
+    while (s != ends) {
+        if (likely(*s > 0 && *s < 128))
+            ++s;
+        else {
+            sa.append(last, s);
+            if (*s != 0) {
+                int c = *s;
+                if (c < 160)
+                    c = windows1252_c1_mapping[c - 128];
+                sa.append_utf8(c);
+            }
+            ++s;
+            last = s;
+        }
+    }
+    goto exit;
+}
+
+/** @brief Return this string's contents encoded for JSON.
+    @pre *this is encoded in UTF-8.
+
+    For instance, String("a\"").encode_json() == "a\\\"". Note that the
+    double-quote characters that usually surround a JSON string are not
+    included. */
+String String::encode_json() const {
+    StringAccum sa;
+    const char* last = encode_json_partial(sa);
+    if (last == begin())
+        return *this;
+    else {
+        sa.append(last, end());
+        return sa.take_string();
+    }
+}
+
+String String::encode_base64(bool pad) const {
+    StringAccum sa;
+    encode_base64(sa, pad);
+    return sa.take_string();
+}
+
+String String::decode_base64() const {
+    StringAccum sa;
+    if (!decode_base64(sa))
+        return String();
+    return sa.take_string();
+}
+
+} // namespace lcdf
diff --git a/silo/masstree/string.hh b/silo/masstree/string.hh
new file mode 100644 (file)
index 0000000..0fe9822
--- /dev/null
@@ -0,0 +1,880 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef LCDF_STRING_HH
+#define LCDF_STRING_HH
+#include "string_base.hh"
+#include <string>
+#include <utility>
+namespace lcdf {
+
+class String : public String_base<String> {
+    struct memo_type;
+  public:
+    struct rep_type;
+
+    enum { max_length = 0x7FFFFFE0 };
+
+    typedef String substring_type;
+    typedef const String& argument_type;
+
+    inline String();
+    inline String(const String &x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline String(String &&x);
+#endif
+    template <typename T>
+    explicit inline String(const String_base<T>& str);
+    inline String(const char* cstr);
+    inline String(const std::string& str);
+    inline String(const char* s, int len);
+    inline String(const unsigned char* s, int len);
+    inline String(const char* first, const char* last);
+    inline String(const unsigned char* first, const unsigned char* last);
+    explicit inline String(bool x);
+    explicit inline String(char c);
+    explicit inline String(unsigned char c);
+    explicit String(int x);
+    explicit String(unsigned x);
+    explicit String(long x);
+    explicit String(unsigned long x);
+    explicit String(long long x);
+    explicit String(unsigned long long x);
+    explicit String(double x);
+    /** @cond never */
+    inline String(const rep_type& r);
+    /** @endcond never */
+    inline ~String();
+
+    static inline const String& make_empty();
+    static inline const String& make_out_of_memory();
+    static String make_uninitialized(int len);
+    static inline String make_stable(const char* cstr);
+    static inline String make_stable(const char* s, int len);
+    static inline String make_stable(const char* first, const char* last);
+    template <typename T>
+    static inline String make_stable(const String_base<T>& str);
+    static String make_fill(int c, int n);
+    static inline const String& make_zero();
+
+    inline const char* data() const;
+    inline int length() const;
+
+    inline const char *c_str() const;
+
+    inline String substring(const char* first, const char* last) const;
+    inline String substring(const unsigned char* first,
+                            const unsigned char* last) const;
+    inline String fast_substring(const char* first, const char* last) const;
+    inline String fast_substring(const unsigned char* first,
+                                 const unsigned char* last) const;
+    String substr(int pos, int len) const;
+    inline String substr(int pos) const;
+    String ltrim() const;
+    String rtrim() const;
+    String trim() const;
+
+    String lower() const;
+    String upper() const;
+    String printable(int type = 0) const;
+    String to_hex() const;
+
+    enum {
+        utf_strip_bom = 1,
+        utf_replacement = 2,
+        utf_prefer_le = 4
+    };
+
+    enum {
+        u_replacement = 0xFFFD
+    };
+
+    String windows1252_to_utf8() const;
+    String utf16be_to_utf8(int flags = 0) const;
+    String utf16le_to_utf8(int flags = 0) const;
+    String utf16_to_utf8(int flags = 0) const;
+    String cesu8_to_utf8(int flags = 0) const;
+    String utf8_to_utf8(int flags = 0) const;
+    String to_utf8(int flags = 0) const;
+
+    using String_base<String>::encode_json;
+    String encode_json() const;
+
+    using String_base<String>::encode_base64;
+    String encode_base64(bool pad = false) const;
+    using String_base<String>::decode_base64;
+    String decode_base64() const;
+
+    inline String& operator=(const String& x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline String& operator=(String&& x);
+#endif
+    template <typename T>
+    inline String& operator=(const String_base<T>& str);
+    inline String& operator=(const char* cstr);
+    inline String& operator=(const std::string& str);
+
+    inline void assign(const String& x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline void assign(String&& x);
+#endif
+    template <typename T>
+    inline void assign(const String_base<T>& str);
+    inline void assign(const char* cstr);
+    inline void assign(const std::string& str);
+    inline void assign(const char* first, const char* last);
+
+    inline void swap(String& x);
+
+    inline void append(const String& x);
+    inline void append(const char* cstr);
+    inline void append(const char* s, int len);
+    inline void append(const char* first, const char* last);
+    inline void append(const unsigned char* first, const unsigned char* last);
+    void append_fill(int c, int len);
+    char *append_uninitialized(int len);
+
+    inline String& operator+=(const String& x);
+    template <typename T>
+    inline String& operator+=(const String_base<T>& x);
+    inline String& operator+=(const char* cstr);
+    inline String& operator+=(char c);
+
+    // String operator+(String, const String &);
+    // String operator+(String, const char *);
+    // String operator+(const char *, const String &);
+
+    inline bool is_shared() const;
+    inline bool is_stable() const;
+
+    inline String unique() const;
+    inline void shrink_to_fit();
+
+    char* mutable_data();
+    inline unsigned char* mutable_udata();
+    char* mutable_c_str();
+
+    void align(int);
+
+    static const unsigned char* skip_utf8_char(const unsigned char* first,
+                                               const unsigned char* last);
+    static const char* skip_utf8_char(const char* first, const char* last);
+    static const unsigned char* skip_utf8_bom(const unsigned char* first,
+                                              const unsigned char* last);
+    static const char* skip_utf8_bom(const char* first, const char* last);
+
+    /** @cond never */
+    struct rep_type {
+        const char* data;
+        int length;
+        int memo_offset;
+
+        inline void ref() const {
+            if (memo_offset)
+                ++xmemo()->refcount;
+        }
+        inline void deref() const {
+            if (memo_offset && --xmemo()->refcount == 0)
+                String::delete_memo(xmemo());
+        }
+        inline void reset_ref() {
+            memo_offset = 0;
+        }
+
+      private:
+        inline memo_type* xmemo() const {
+            return reinterpret_cast<memo_type*>
+                (const_cast<char*>(data + memo_offset));
+        }
+        inline memo_type* memo() const {
+            return memo_offset ? xmemo() : nullptr;
+        }
+        inline void assign(const char* d, int l, memo_type* m) {
+            data = d;
+            length = l;
+            if (m) {
+                ++m->refcount;
+                memo_offset = static_cast<int>(reinterpret_cast<char*>(m) - d);
+            } else
+                memo_offset = 0;
+        }
+        inline void assign_noref(const char* d, int l, memo_type* m) {
+            data = d;
+            length = l;
+            if (m)
+                memo_offset = static_cast<int>(reinterpret_cast<char*>(m) - d);
+            else
+                memo_offset = 0;
+        }
+        friend class String;
+        friend class StringAccum;
+    };
+
+    const rep_type& internal_rep() const {
+        return _r;
+    }
+    void swap(rep_type& other_rep) {
+        using std::swap;
+        swap(_r, other_rep);
+    }
+    inline void assign(const rep_type& rep);
+    /** @endcond never */
+
+  private:
+    /** @cond never */
+    struct memo_type {
+        volatile uint32_t refcount;
+        uint32_t capacity;
+        volatile uint32_t dirty;
+#if HAVE_STRING_PROFILING > 1
+        memo_type** pprev;
+        memo_type* next;
+#endif
+        char real_data[8];      // but it might be more or less
+
+        inline void initialize(uint32_t capacity, uint32_t dirty);
+#if HAVE_STRING_PROFILING
+        void account_new();
+        void account_destroy();
+#else
+        inline void account_destroy() {}
+#endif
+    };
+
+    enum {
+        MEMO_SPACE = sizeof(memo_type) - 8 /* == sizeof(memo_type::real_data) */
+    };
+
+    struct null_memo {
+    };
+    /** @endcond never */
+
+    mutable rep_type _r;        // mutable for c_str()
+
+#if HAVE_STRING_PROFILING
+    static uint64_t live_memo_count;
+    static uint64_t memo_sizes[55];
+    static uint64_t live_memo_sizes[55];
+    static uint64_t live_memo_bytes[55];
+# if HAVE_STRING_PROFILING > 1
+    static memo_t *live_memos[55];
+# endif
+
+    static inline int profile_memo_size_bucket(uint32_t dirty, uint32_t capacity) {
+        if (capacity <= 16)
+            return dirty;
+        else if (capacity <= 32)
+            return 17 + (capacity - 17) / 2;
+        else if (capacity <= 64)
+            return 25 + (capacity - 33) / 8;
+        else
+            return 29 + 26 - ffs_msb(capacity - 1);
+    }
+
+    static void profile_update_memo_dirty(memo_type* memo, uint32_t old_dirty, uint32_t new_dirty, uint32_t capacity) {
+        if (capacity <= 16 && new_dirty != old_dirty) {
+            ++memo_sizes[new_dirty];
+            ++live_memo_sizes[new_dirty];
+            live_memo_bytes[new_dirty] += capacity;
+            --live_memo_sizes[old_dirty];
+            live_memo_bytes[old_dirty] -= capacity;
+# if HAVE_STRING_PROFILING > 1
+            if ((*memo->pprev = memo->next))
+                memo->next->pprev = memo->pprev;
+            memo->pprev = &live_memos[new_dirty];
+            if ((memo->next = *memo->pprev))
+                memo->next->pprev = &memo->next;
+            *memo->pprev = memo;
+# else
+            (void) memo;
+# endif
+        }
+    }
+
+    static void one_profile_report(StringAccum &sa, int i, int examples);
+#endif
+
+    inline String(const char* data, int length, memo_type* memo) {
+        _r.assign_noref(data, length, memo);
+    }
+    inline String(const char* data, int length, const null_memo&)
+        : _r{data, length, 0} {
+    }
+
+    inline void deref() const {
+        _r.deref();
+    }
+
+    void assign(const char* s, int len, bool need_deref);
+    void assign_out_of_memory();
+    void append(const char* s, int len, memo_type* memo);
+    static String hard_make_stable(const char *s, int len);
+    static inline memo_type* absent_memo() {
+        return reinterpret_cast<memo_type*>(uintptr_t(1));
+    }
+    static inline memo_type* create_memo(int capacity, int dirty);
+    static void delete_memo(memo_type* memo);
+    const char* hard_c_str() const;
+    bool hard_equals(const char* s, int len) const;
+
+    static const char int_data[20];
+    static const rep_type null_string_rep;
+    static const rep_type oom_string_rep;
+    static const rep_type zero_string_rep;
+
+    static int parse_cesu8_char(const unsigned char* s,
+                                const unsigned char* end);
+
+    friend struct rep_type;
+    friend class StringAccum;
+};
+
+
+/** @cond never */
+inline void String::memo_type::initialize(uint32_t capacity, uint32_t dirty) {
+    this->refcount = 1;
+    this->capacity = capacity;
+    this->dirty = dirty;
+#if HAVE_STRING_PROFILING
+    this->account_new();
+#endif
+}
+/** @endcond never */
+
+/** @brief Construct an empty String (with length 0). */
+inline String::String()
+    : _r{String_generic::empty_data, 0, 0} {
+}
+
+/** @brief Construct a copy of the String @a x. */
+inline String::String(const String& x)
+    : _r(x._r) {
+    _r.ref();
+}
+
+#if HAVE_CXX_RVALUE_REFERENCES
+/** @brief Move-construct a String from @a x. */
+inline String::String(String &&x)
+    : _r(x._r) {
+    x._r.reset_ref();
+}
+#endif
+
+/** @brief Construct a copy of the string @a str. */
+template <typename T>
+inline String::String(const String_base<T> &str) {
+    assign(str.data(), str.length(), false);
+}
+
+/** @brief Construct a String containing the C string @a cstr.
+    @param cstr a null-terminated C string
+    @return A String containing the characters of @a cstr, up to but not
+    including the terminating null character. */
+inline String::String(const char* cstr) {
+    if (LCDF_CONSTANT_CSTR(cstr))
+        _r.assign(cstr, strlen(cstr), 0);
+    else
+        assign(cstr, -1, false);
+}
+
+/** @brief Construct a String containing the first @a len characters of
+    string @a s.
+    @param s a string
+    @param len number of characters to take from @a s.  If @a len @< 0,
+    then takes @c strlen(@a s) characters.
+    @return A String containing @a len characters of @a s. */
+inline String::String(const char* s, int len) {
+    assign(s, len, false);
+}
+
+/** @overload */
+inline String::String(const unsigned char* s, int len) {
+    assign(reinterpret_cast<const char*>(s), len, false);
+}
+
+/** @brief Construct a String containing the characters from @a first
+    to @a last.
+    @param first first character in string (begin iterator)
+    @param last pointer one past last character in string (end iterator)
+    @return A String containing the characters from @a first to @a last.
+
+    Constructs an empty string if @a first @>= @a last. */
+inline String::String(const char *first, const char *last) {
+    assign(first, (first < last ? last - first : 0), false);
+}
+
+/** @overload */
+inline String::String(const unsigned char* first, const unsigned char* last) {
+    assign(reinterpret_cast<const char*>(first),
+           (first < last ? last - first : 0), false);
+}
+
+/** @brief Construct a String from a std::string. */
+inline String::String(const std::string& str) {
+    assign(str.data(), str.length(), false);
+}
+
+/** @brief Construct a String equal to "true" or "false" depending on the
+    value of @a x. */
+inline String::String(bool x)
+    : _r{String_generic::bool_data + (-x & 6), 5 - x, 0} {
+    // bool_data equals "false\0true\0"
+}
+
+/** @brief Construct a String containing the single character @a c. */
+inline String::String(char c) {
+    assign(&c, 1, false);
+}
+
+/** @overload */
+inline String::String(unsigned char c) {
+    assign(reinterpret_cast<char*>(&c), 1, false);
+}
+
+inline String::String(const rep_type& r)
+    : _r(r) {
+    _r.ref();
+}
+
+/** @brief Destroy a String, freeing memory if necessary. */
+inline String::~String() {
+    deref();
+}
+
+/** @brief Return a const reference to an empty String.
+
+    May be quicker than String::String(). */
+inline const String& String::make_empty() {
+    return reinterpret_cast<const String &>(null_string_rep);
+}
+
+/** @brief Return a String containing @a len unknown characters. */
+inline String String::make_uninitialized(int len) {
+    String s;
+    s.append_uninitialized(len);
+    return s;
+}
+
+/** @brief Return a const reference to the string "0". */
+inline const String& String::make_zero() {
+    return reinterpret_cast<const String&>(zero_string_rep);
+}
+
+/** @brief Return a String that directly references the C string @a cstr.
+
+    The make_stable() functions are suitable for static constant strings
+    whose data is known to stay around forever, such as C string constants.
+
+    @warning The String implementation may access @a cstr's terminating null
+    character. */
+inline String String::make_stable(const char *cstr) {
+    if (LCDF_CONSTANT_CSTR(cstr))
+        return String(cstr, strlen(cstr), null_memo());
+    else
+        return hard_make_stable(cstr, -1);
+}
+
+/** @brief Return a String that directly references the first @a len
+    characters of @a s.
+
+    If @a len @< 0, treats @a s as a null-terminated C string.
+
+    @warning The String implementation may access @a s[@a len], which
+    should remain constant even though it's not part of the String. */
+inline String String::make_stable(const char* s, int len) {
+    if (__builtin_constant_p(len) && len >= 0)
+        return String(s, len, null_memo());
+    else
+        return hard_make_stable(s, len);
+}
+
+/** @brief Return a String that directly references the character data in
+    [@a first, @a last).
+    @param first pointer to the first character in the character data
+    @param last pointer one beyond the last character in the character data
+    (but see the warning)
+
+    This function is suitable for static constant strings whose data is
+    known to stay around forever, such as C string constants.  Returns an
+    empty string if @a first @>= @a last.
+
+    @warning The String implementation may access *@a last, which should
+    remain constant even though it's not part of the String. */
+inline String String::make_stable(const char* first, const char* last) {
+    return String(first, (first < last ? last - first : 0), null_memo());
+}
+
+/** @overload */
+template <typename T>
+inline String String::make_stable(const String_base<T>& str) {
+    return String(str.data(), str.length(), null_memo());
+}
+
+/** @brief Return a pointer to the string's data.
+
+    Only the first length() characters are valid, and the string
+    might not be null-terminated. */
+inline const char* String::data() const {
+    return _r.data;
+}
+
+/** @brief Return the string's length. */
+inline int String::length() const {
+    return _r.length;
+}
+
+/** @brief Null-terminate the string.
+
+    The terminating null character isn't considered part of the string, so
+    this->length() doesn't change.  Returns a corresponding C string
+    pointer.  The returned pointer is semi-temporary; it will persist until
+    the string is destroyed or appended to. */
+inline const char* String::c_str() const {
+    // See also hard_c_str().
+#if HAVE_OPTIMIZE_SIZE || __OPTIMIZE_SIZE__
+    return hard_c_str();
+#else
+    // We may already have a '\0' in the right place.  If _memo has no
+    // capacity, then this is one of the special strings (null or
+    // stable). We are guaranteed, in these strings, that _data[_length]
+    // exists. Otherwise must check that _data[_length] exists.
+    const char* end_data = _r.data + _r.length;
+    memo_type* m = _r.memo();
+    if ((m && end_data >= m->real_data + m->dirty)
+        || *end_data != '\0') {
+        if (char *x = const_cast<String*>(this)->append_uninitialized(1)) {
+            *x = '\0';
+            --_r.length;
+        }
+    }
+    return _r.data;
+#endif
+}
+
+/** @brief Return a substring of the current string starting at @a first
+    and ending before @a last.
+    @param first pointer to the first substring character
+    @param last pointer one beyond the last substring character
+
+    Returns an empty string if @a first @>= @a last. Also returns an empty
+    string if @a first or @a last is out of range (i.e., either less than
+    this->begin() or greater than this->end()), but this should be
+    considered a programming error; a future version may generate a warning
+    for this case. */
+inline String String::substring(const char* first, const char* last) const {
+    if (first < last && first >= _r.data && last <= _r.data + _r.length) {
+        _r.ref();
+        return String(first, last - first, _r.memo());
+    } else
+        return String();
+}
+/** @overload */
+inline String String::substring(const unsigned char* first, const unsigned char* last) const {
+    return substring(reinterpret_cast<const char*>(first),
+                     reinterpret_cast<const char*>(last));
+}
+
+/** @brief Return a substring of the current string starting at @a first
+    and ending before @a last.
+    @param first pointer to the first substring character
+    @param last pointer one beyond the last substring character
+    @pre begin() <= @a first <= @a last <= end() */
+inline String String::fast_substring(const char* first, const char* last) const {
+    assert(begin() <= first && first <= last && last <= end());
+    _r.ref();
+    return String(first, last - first, _r.memo());
+}
+/** @overload */
+inline String String::fast_substring(const unsigned char* first, const unsigned char* last) const {
+    return fast_substring(reinterpret_cast<const char*>(first),
+                          reinterpret_cast<const char*>(last));
+}
+
+/** @brief Return the suffix of the current string starting at index @a pos.
+
+    If @a pos is negative, starts that far from the end of the string.
+    If @a pos is so negative that the suffix starts outside the string,
+    then the entire string is returned. If the substring is beyond the
+    end of the string (@a pos > length()), returns an empty string (but
+    this should be considered a programming error; a future version may
+    generate a warning for this case).
+
+    @note String::substr() is intended to behave like Perl's
+    substr(). */
+inline String String::substr(int pos) const {
+    return substr((pos <= -_r.length ? 0 : pos), _r.length);
+}
+
+inline void String::assign(const rep_type& rep) {
+    rep.ref();
+    _r.deref();
+    _r = rep;
+}
+
+/** @brief Assign this string to @a x. */
+inline void String::assign(const String& x) {
+    assign(x._r);
+}
+
+/** @brief Assign this string to @a x. */
+inline String& String::operator=(const String& x) {
+    assign(x);
+    return *this;
+}
+
+#if HAVE_CXX_RVALUE_REFERENCES
+/** @brief Move-assign this string to @a x. */
+inline void String::assign(String&& x) {
+    deref();
+    _r = x._r;
+    x._r.reset_ref();
+}
+
+/** @brief Move-assign this string to @a x. */
+inline String& String::operator=(String&& x) {
+    assign(std::move(x));
+    return *this;
+}
+#endif
+
+/** @brief Assign this string to the C string @a cstr. */
+inline void String::assign(const char* cstr) {
+    if (LCDF_CONSTANT_CSTR(cstr)) {
+        deref();
+        _r.assign(cstr, strlen(cstr), 0);
+    } else
+        assign(cstr, -1, true);
+}
+
+/** @brief Assign this string to the C string @a cstr. */
+inline String& String::operator=(const char* cstr) {
+    assign(cstr);
+    return *this;
+}
+
+/** @brief Assign this string to the string @a str. */
+template <typename T>
+inline void String::assign(const String_base<T>& str) {
+    assign(str.data(), str.length(), true);
+}
+
+/** @brief Assign this string to the string @a str. */
+template <typename T>
+inline String& String::operator=(const String_base<T>& str) {
+    assign(str);
+    return *this;
+}
+
+/** @brief Assign this string to the std::string @a str. */
+inline void String::assign(const std::string& str) {
+    assign(str.data(), str.length(), true);
+}
+
+/** @brief Assign this string to the std::string @a str. */
+inline String& String::operator=(const std::string& str) {
+    assign(str);
+    return *this;
+}
+
+/** @brief Assign this string to string [@a first, @a last). */
+inline void String::assign(const char *first, const char *last) {
+    assign(first, last - first, true);
+}
+
+/** @brief Swap the values of this string and @a x. */
+inline void String::swap(String &x) {
+    using std::swap;
+    swap(_r, x._r);
+}
+
+/** @brief Append @a x to this string. */
+inline void String::append(const String& x) {
+    append(x.data(), x.length(), x._r.memo());
+}
+
+/** @brief Append the null-terminated C string @a cstr to this string.
+    @param cstr data to append */
+inline void String::append(const char* cstr) {
+    if (LCDF_CONSTANT_CSTR(cstr))
+        append(cstr, strlen(cstr), absent_memo());
+    else
+        append(cstr, -1, absent_memo());
+}
+
+/** @brief Append the first @a len characters of @a s to this string.
+    @param s data to append
+    @param len length of data
+
+    If @a len @< 0, treats @a s as a null-terminated C string. */
+inline void String::append(const char* s, int len) {
+    append(s, len, absent_memo());
+}
+
+/** @brief Appends the data from @a first to @a last to this string.
+
+    Does nothing if @a first @>= @a last. */
+inline void String::append(const char* first, const char* last) {
+    if (first < last)
+        append(first, last - first);
+}
+/** @overload */
+inline void String::append(const unsigned char* first,
+                           const unsigned char* last) {
+    if (first < last)
+        append(reinterpret_cast<const char*>(first), last - first);
+}
+
+/** @brief Append @a x to this string.
+    @return *this */
+inline String& String::operator+=(const String &x) {
+    append(x.data(), x.length(), x._r.memo());
+    return *this;
+}
+
+/** @brief Append the null-terminated C string @a cstr to this string.
+    @return *this */
+inline String& String::operator+=(const char* cstr) {
+    append(cstr);
+    return *this;
+}
+
+/** @brief Append the character @a c to this string.
+    @return *this */
+inline String &String::operator+=(char c) {
+    append(&c, 1);
+    return *this;
+}
+
+/** @brief Append the string @a x to this string.
+    @return *this */
+template <typename T>
+inline String &String::operator+=(const String_base<T> &x) {
+    append(x.data(), x.length());
+    return *this;
+}
+
+/** @brief Test if the String's data is shared or stable. */
+inline bool String::is_shared() const {
+    memo_type* m = _r.memo();
+    return !m || m->refcount != 1;
+}
+
+/** @brief Test if the String's data is stable. */
+inline bool String::is_stable() const {
+    return !_r.memo();
+}
+
+/** @brief Return a unique version of this String.
+
+    The return value shares no data with any other non-stable String. */
+inline String String::unique() const {
+    memo_type* m = _r.memo();
+    if (!m || m->refcount == 1)
+        return *this;
+    else
+        return String(_r.data, _r.data + _r.length);
+}
+
+/** @brief Reduce the memory allocation for this String.
+
+    After calling this function, this String shares no more than 256 bytes
+    of data with any other non-stable String. */
+inline void String::shrink_to_fit() {
+    memo_type* m = _r.memo();
+    if (m && m->refcount > 1 && (uint32_t) _r.length + 256 < m->capacity)
+        *this = String(_r.data, _r.data + _r.length);
+}
+
+/** @brief Return the unsigned char* version of mutable_data(). */
+inline unsigned char* String::mutable_udata() {
+    return reinterpret_cast<unsigned char*>(mutable_data());
+}
+
+/** @brief Return a const reference to a canonical out-of-memory String. */
+inline const String &String::make_out_of_memory() {
+    return reinterpret_cast<const String &>(oom_string_rep);
+}
+
+/** @brief Return a pointer to the next character in UTF-8 encoding.
+    @pre @a first @< @a last
+
+    If @a first doesn't point at a valid UTF-8 character, returns @a first. */
+inline const char* String::skip_utf8_char(const char* first, const char* last) {
+    return reinterpret_cast<const char*>(
+        skip_utf8_char(reinterpret_cast<const unsigned char*>(first),
+                       reinterpret_cast<const unsigned char*>(last)));
+}
+
+inline const unsigned char* String::skip_utf8_bom(const unsigned char* first,
+                                                  const unsigned char* last) {
+    if (last - first >= 3
+        && first[0] == 0xEF && first[1] == 0xBB && first[2] == 0xBF)
+        return first + 3;
+    else
+        return first;
+}
+
+inline const char* String::skip_utf8_bom(const char* first, const char* last) {
+    return reinterpret_cast<const char*>(
+        skip_utf8_bom(reinterpret_cast<const unsigned char*>(first),
+                      reinterpret_cast<const unsigned char*>(last)));
+}
+
+
+/** @relates String
+    @brief Concatenate the operands and return the result.
+
+    At most one of the two operands can be a null-terminated C string. */
+inline String operator+(String a, const String& b) {
+    a += b;
+    return a;
+}
+
+/** @relates String */
+inline String operator+(String a, const char* b) {
+    a.append(b);
+    return a;
+}
+
+/** @relates String */
+inline String operator+(const char* a, const String& b) {
+    String s1(a);
+    s1 += b;
+    return s1;
+}
+
+/** @relates String
+    @brief Concatenate the operands and return the result.
+
+    The second operand is a single character. */
+inline String operator+(String a, char b) {
+    a.append(&b, 1);
+    return a;
+}
+
+#if HAVE_CXX_USER_LITERALS
+inline String operator"" _S(const char* s, size_t len) {
+    return String::make_stable(s, s + len);
+}
+#endif
+
+inline void swap(String& a, String& b) {
+    a.swap(b);
+}
+
+} // namespace lcdf
+
+LCDF_MAKE_STRING_HASH(lcdf::String)
+#endif
diff --git a/silo/masstree/string_base.hh b/silo/masstree/string_base.hh
new file mode 100644 (file)
index 0000000..9bdf17f
--- /dev/null
@@ -0,0 +1,628 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef STRING_BASE_HH
+#define STRING_BASE_HH
+#include "compiler.hh"
+#include "hashcode.hh"
+#include <assert.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <iostream>
+namespace lcdf {
+class StringAccum;
+#define LCDF_CONSTANT_CSTR(cstr) ((cstr) && __builtin_constant_p(strlen((cstr))))
+
+class String_generic {
+  public:
+    static const char empty_data[1];
+    static const char bool_data[11]; // "false\0true\0"
+    static const char out_of_memory_data[15];
+    static const char base64_encoding_table[65];
+    static const unsigned char base64_decoding_map[256];
+    enum { out_of_memory_length = 14 };
+    static bool out_of_memory(const char* s) {
+        return unlikely(s >= out_of_memory_data
+                        && s <= out_of_memory_data + out_of_memory_length);
+    }
+    static bool equals(const char* a, int a_len, const char* b, int b_len) {
+        return a_len == b_len && memcmp(a, b, a_len) == 0;
+    }
+    static int compare(const char* a, int a_len, const char* b, int b_len);
+    static inline int compare(const unsigned char* a, int a_len,
+                              const unsigned char* b, int b_len) {
+        return compare(reinterpret_cast<const char*>(a), a_len,
+                       reinterpret_cast<const char*>(b), b_len);
+    }
+    static int natural_compare(const char* a, int a_len, const char* b, int b_len);
+    static int natural_compare(const unsigned char* a, int a_len,
+                               const unsigned char* b, int b_len) {
+        return natural_compare(reinterpret_cast<const char*>(a), a_len,
+                               reinterpret_cast<const char*>(b), b_len);
+    }
+    static bool starts_with(const char *a, int a_len, const char *b, int b_len) {
+        return a_len >= b_len && memcmp(a, b, b_len) == 0;
+    }
+    static int find_left(const char *s, int len, int start, char x);
+    static int find_left(const char *s, int len, int start, const char *x, int x_len);
+    static int find_right(const char *s, int len, int start, char x);
+    static int find_right(const char *s, int len, int start, const char *x, int x_len);
+    static bool glob_match(const char* s, int slen, const char* pattern, int plen);
+    template <typename T> static inline typename T::substring_type ltrim(const T &str);
+    template <typename T> static inline typename T::substring_type rtrim(const T &str);
+    template <typename T> static inline typename T::substring_type trim(const T &str);
+    static hashcode_t hashcode(const char *s, int len);
+    static hashcode_t hashcode(const char *first, const char *last) {
+        return hashcode(first, last - first);
+    }
+    static long to_i(const char* first, const char* last);
+};
+
+template <typename T>
+class String_base {
+  public:
+    typedef T type;
+    typedef const char* const_iterator;
+    typedef const_iterator iterator;
+    typedef const unsigned char* const_unsigned_iterator;
+    typedef const_unsigned_iterator unsigned_iterator;
+    typedef int (String_base<T>::*unspecified_bool_type)() const;
+
+    const char* data() const {
+        return static_cast<const T*>(this)->data();
+    }
+    int length() const {
+        return static_cast<const T*>(this)->length();
+    }
+
+    /** @brief Return a pointer to the string's data as unsigned chars.
+
+        Only the first length() characters are valid, and the string data
+        might not be null-terminated. @sa data() */
+    const unsigned char* udata() const {
+        return reinterpret_cast<const unsigned char*>(data());
+    }
+    /** @brief Return an iterator for the beginning of the string.
+
+        String iterators are simply pointers into string data, so they are
+        quite efficient. @sa String::data */
+    const_iterator begin() const {
+        return data();
+    }
+    /** @brief Return an iterator for the end of the string.
+
+        The result points one character beyond the last character in the
+        string. */
+    const_iterator end() const {
+        return data() + length();
+    }
+    /** @brief Return an unsigned iterator for the beginning of the string.
+
+        This is equivalent to reinterpret_cast<const unsigned char
+        *>(begin()). */
+    const_unsigned_iterator ubegin() const {
+        return reinterpret_cast<const_unsigned_iterator>(data());
+    }
+    /** @brief Return an unsigned iterator for the end of the string.
+
+        This is equivalent to reinterpret_cast<const unsigned char
+        *>(end()). */
+    const_unsigned_iterator uend() const {
+        return reinterpret_cast<const_unsigned_iterator>(data() + length());
+    }
+    /** @brief Test if the string is nonempty. */
+    operator unspecified_bool_type() const {
+        return length() ? &String_base<T>::length : 0;
+    }
+    /** @brief Test if the string is empty. */
+    bool operator!() const {
+        return length() == 0;
+    }
+    /** @brief Test if the string is empty. */
+    bool empty() const {
+        return length() == 0;
+    }
+    /** @brief Test if the string is an out-of-memory string. */
+    bool out_of_memory() const {
+        return String_generic::out_of_memory(data());
+    }
+    /** @brief Return the @a i th character in the string.
+
+        Does not check bounds. @sa at() */
+    const char& operator[](int i) const {
+        return data()[i];
+    }
+    /** @brief Return the @a i th character in the string.
+
+        Checks bounds: an assertion will fail if @a i is less than 0 or not
+        less than length(). @sa operator[] */
+    const char& at(int i) const {
+        assert(unsigned(i) < unsigned(length()));
+        return data()[i];
+    }
+    /** @brief Return the first character in the string.
+
+        Does not check bounds. Same as (*this)[0]. */
+    const char& front() const {
+        return data()[0];
+    }
+    /** @brief Return the last character in the string.
+
+        Does not check bounds. Same as (*this)[length() - 1]. */
+    const char& back() const {
+        return data()[length() - 1];
+    }
+    /** @brief Test if this string is equal to the C string @a cstr. */
+    bool equals(const char *cstr) const {
+        return String_generic::equals(data(), length(), cstr, strlen(cstr));
+    }
+    /** @brief Test if this string is equal to the first @a len characters
+        of @a s. */
+    bool equals(const char *s, int len) const {
+        return String_generic::equals(data(), length(), s, len);
+    }
+    /** @brief Test if this string is equal to @a x. */
+    template <typename TT>
+    bool equals(const String_base<TT>& x) const {
+        return String_generic::equals(data(), length(), x.data(), x.length());
+    }
+    /** @brief Compare this string with the C string @a cstr.
+
+        Returns 0 if this string equals @a cstr, negative if this string is
+        less than @a cstr in lexicographic order, and positive if this
+        string is greater than @a cstr. Lexicographic order treats
+        characters as unsigned. */
+    int compare(const char* cstr) const {
+        return String_generic::compare(data(), length(), cstr, strlen(cstr));
+    }
+    /** @brief Compare this string with the first @a len characters of @a
+        s. */
+    int compare(const char* s, int len) const {
+        return String_generic::compare(data(), length(), s, len);
+    }
+    /** @brief Compare this string with @a x. */
+    template <typename TT>
+    int compare(const String_base<TT>& x) const {
+        return String_generic::compare(data(), length(), x.data(), x.length());
+    }
+    /** @brief Compare strings @a a and @a b. */
+    template <typename TT, typename UU>
+    static int compare(const String_base<TT>& a, const String_base<UU>& b) {
+        return String_generic::compare(a.data(), a.length(), b.data(), b.length());
+    }
+    /** @brief Compare strings @a a and @a b. */
+    template <typename UU>
+    static int compare(const char* a, const String_base<UU> &b) {
+        return String_generic::compare(a, strlen(a), b.data(), b.length());
+    }
+    /** @brief Compare strings @a a and @a b. */
+    template <typename TT>
+    static int compare(const String_base<TT>& a, const char* b) {
+        return String_generic::compare(a.data(), a.length(), b, strlen(b));
+    }
+    /** @brief Compare strings @a a and @a b. */
+    static int compare(const char* a, const char* b) {
+        return String_generic::compare(a, strlen(a), b, strlen(b));
+    }
+    /** @brief Compare this string with the C string @a cstr using natural order.
+
+        Natural string comparison attempts to order embedded decimal number
+        reprepresentations according to their numeric order; thus, the
+        string "100" compares greater than the string "2". Returns 0 if this
+        string equals @a cstr (only possible if the strings are
+        byte-for-byte identical), negative if this string is less than @a
+        cstr in natural order, and positive if this string is greater
+        than @a cstr in natural order. */
+    int natural_compare(const char *cstr) const {
+        return String_generic::natural_compare(data(), length(), cstr, strlen(cstr));
+    }
+    /** @brief Compare this string with the first @a len characters of @a
+        s using natural order. */
+    int natural_compare(const char *s, int len) const {
+        return String_generic::natural_compare(data(), length(), s, len);
+    }
+    /** @brief Compare this string with @a x using natural order. */
+    template <typename TT>
+    int natural_compare(const String_base<TT> &x) const {
+        return String_generic::natural_compare(data(), length(), x.data(), x.length());
+    }
+    /** @brief Compare strings @a a and @a b using natural order. */
+    template <typename TT, typename UU>
+    static int natural_compare(const String_base<TT> &a, const String_base<UU> &b) {
+        return String_generic::natural_compare(a.data(), a.length(), b.data(), b.length());
+    }
+    /** @brief Compare strings @a a and @a b using natural order. */
+    template <typename UU>
+    static int natural_compare(const char* a, const String_base<UU> &b) {
+        return String_generic::natural_compare(a, strlen(a), b.data(), b.length());
+    }
+    /** @brief Compare strings @a a and @a b using natural order. */
+    template <typename TT>
+    static int natural_compare(const String_base<TT>& a, const char* b) {
+        return String_generic::natural_compare(a.data(), a.length(), b, strlen(b));
+    }
+    /** @brief Compare strings @a a and @a b using natural order. */
+    static int natural_compare(const char* a, const char* b) {
+        return String_generic::natural_compare(a, strlen(a), b, strlen(b));
+    }
+    /** @brief Compare strings @a a and @a b using natural order. */
+    static int natural_compare(const std::string& a, const std::string& b) {
+        return String_generic::natural_compare(a.data(), a.length(), b.data(), b.length());
+    }
+    /** @brief Comparator function object for natural string comparison. */
+    class natural_comparator {
+      public:
+        template <typename TT, typename UU>
+        bool operator()(const TT& a, const UU& b) const {
+            return String_base<T>::natural_compare(a, b) < 0;
+        }
+    };
+    /** @brief Test if this string begins with the C string @a cstr. */
+    bool starts_with(const char *cstr) const {
+        return String_generic::starts_with(data(), length(), cstr, strlen(cstr));
+    }
+    /** @brief Test if this string begins with the first @a len characters
+        of @a s. */
+    bool starts_with(const char *s, int len) const {
+        return String_generic::starts_with(data(), length(), s, len);
+    }
+    /** @brief Test if this string begins with @a x. */
+    template <typename TT>
+    bool starts_with(const String_base<TT> &x) const {
+        return String_generic::starts_with(data(), length(), x.data(), x.length());
+    }
+    /** @brief Search for a character in this string.
+
+        Return the index of the leftmost occurrence of @a c, starting at
+        index @a start and working up to the end of the string. Return -1 if
+        the character is not found. */
+    int find_left(char x, int start = 0) const {
+        return String_generic::find_left(data(), length(), start, x);
+    }
+    /** @brief Search for the C string @a cstr as a substring in this string.
+
+        Return the index of the leftmost occurrence of @a cstr, starting at
+        index @a start. Return -1 if the substring is not found. */
+    int find_left(const char *cstr, int start = 0) const {
+        return String_generic::find_left(data(), length(), start, cstr, strlen(cstr));
+    }
+    /** @brief Search for @a x as a substring in this string.
+
+        Return the index of the leftmost occurrence of @a x, starting at
+        index @a start. Return -1 if the substring is not found. */
+    template <typename TT>
+    int find_left(const String_base<TT> &x, int start = 0) const {
+        return String_generic::find_left(data(), length(), start, x.data(), x.length());
+    }
+    /** @brief Search backwards for a character in this string.
+
+        Return the index of the rightmost occurrence of @a c, starting at
+        index @a start and working up to the end of the string. Return -1 if
+        the character is not found. */
+    int find_right(char c, int start = INT_MAX) const {
+        return String_generic::find_right(data(), length(), start, c);
+    }
+    /** @brief Search backwards for the C string @a cstr as a substring in
+        this string.
+
+        Return the index of the rightmost occurrence of @a cstr, starting
+        at index @a start. Return -1 if the substring is not found. */
+    int find_right(const char *cstr, int start = INT_MAX) const {
+        return String_generic::find_right(data(), length(), start, cstr, strlen(cstr));
+    }
+    /** @brief Search backwards for @a x as a substring in this string.
+
+        Return the index of the rightmost occurrence of @a x, starting at
+        index @a start. Return -1 if the substring is not found. */
+    template <typename TT>
+    int find_right(const String_base<TT> &x, int start = INT_MAX) const {
+        return String_generic::find_right(data(), length(), start, x.data(), x.length());
+    }
+    /** @brief Test if this string matches the glob @a pattern.
+
+        Glob pattern syntax allows * (any number of characters), ? (one
+        arbitrary character), [] (character classes, possibly negated), and
+        \\ (escaping). */
+    bool glob_match(const char* pattern) const {
+        return String_generic::glob_match(data(), length(), pattern, strlen(pattern));
+    }
+    /** @overload */
+    template <typename TT>
+    bool glob_match(const String_base<TT>& pattern) const {
+        return String_generic::glob_match(data(), length(), pattern.data(), pattern.length());
+    }
+    /** @brief Return a 32-bit hash function of the characters in [@a first, @a last).
+
+        Uses Paul Hsieh's "SuperFastHash" algorithm, described at
+        http://www.azillionmonkeys.com/qed/hash.html
+        This hash function uses all characters in the string.
+
+        @invariant If last1 - first1 == last2 - first2 and memcmp(first1,
+        first2, last1 - first1) == 0, then hashcode(first1, last1) ==
+        hashcode(first2, last2). */
+    static hashcode_t hashcode(const char *first, const char *last) {
+        return String_generic::hashcode(first, last);
+    }
+    /** @brief Return a 32-bit hash function of the characters in this string. */
+    hashcode_t hashcode() const {
+        return String_generic::hashcode(data(), length());
+    }
+
+    /** @brief Return the integer value of this string. */
+    long to_i() const {
+        return String_generic::to_i(begin(), end());
+    }
+
+    template <typename E>
+    const_iterator encode_json_partial(E& e) const;
+    template <typename E>
+    inline void encode_json(E& e) const;
+    template <typename E>
+    void encode_base64(E& e, bool pad = false) const;
+    template <typename E>
+    bool decode_base64(E& e) const;
+
+    /** @brief Return this string as a std::string. */
+    inline operator std::string() const {
+        return std::string(begin(), end());
+    }
+
+  protected:
+    String_base() = default;
+};
+
+template <typename T, typename U>
+inline bool operator==(const String_base<T> &a, const String_base<U> &b) {
+    return a.equals(b);
+}
+
+template <typename T>
+inline bool operator==(const String_base<T> &a, const std::string &b) {
+    return a.equals(b.data(), b.length());
+}
+
+template <typename T>
+inline bool operator==(const std::string &a, const String_base<T> &b) {
+    return b.equals(a.data(), a.length());
+}
+
+template <typename T>
+inline bool operator==(const String_base<T> &a, const char *b) {
+    return a.equals(b, strlen(b));
+}
+
+template <typename T>
+inline bool operator==(const char *a, const String_base<T> &b) {
+    return b.equals(a, strlen(a));
+}
+
+template <typename T, typename U>
+inline bool operator!=(const String_base<T> &a, const String_base<U> &b) {
+    return !(a == b);
+}
+
+template <typename T>
+inline bool operator!=(const String_base<T> &a, const std::string &b) {
+    return !(a == b);
+}
+
+template <typename T>
+inline bool operator!=(const std::string &a, const String_base<T> &b) {
+    return !(a == b);
+}
+
+template <typename T>
+inline bool operator!=(const String_base<T> &a, const char *b) {
+    return !(a == b);
+}
+
+template <typename T>
+inline bool operator!=(const char *a, const String_base<T> &b) {
+    return !(a == b);
+}
+
+template <typename T, typename U>
+inline bool operator<(const String_base<T> &a, const String_base<U> &b) {
+    return a.compare(b) < 0;
+}
+
+template <typename T, typename U>
+inline bool operator<=(const String_base<T> &a, const String_base<U> &b) {
+    return a.compare(b) <= 0;
+}
+
+template <typename T, typename U>
+inline bool operator>=(const String_base<T> &a, const String_base<U> &b) {
+    return a.compare(b) >= 0;
+}
+
+template <typename T, typename U>
+inline bool operator>(const String_base<T> &a, const String_base<U> &b) {
+    return a.compare(b) > 0;
+}
+
+template <typename T>
+inline std::ostream &operator<<(std::ostream &f, const String_base<T> &str) {
+    return f.write(str.data(), str.length());
+}
+
+template <typename T>
+inline hashcode_t hashcode(const String_base<T>& x) {
+    return String_generic::hashcode(x.data(), x.length());
+}
+
+// boost's spelling
+template <typename T>
+inline size_t hash_value(const String_base<T>& x) {
+    return String_generic::hashcode(x.data(), x.length());
+}
+
+template <typename T>
+inline typename T::substring_type String_generic::ltrim(const T &str) {
+    const char *b = str.begin(), *e = str.end();
+    while (b != e && isspace((unsigned char) b[0]))
+        ++b;
+    return str.fast_substring(b, e);
+}
+
+template <typename T>
+inline typename T::substring_type String_generic::rtrim(const T &str) {
+    const char *b = str.begin(), *e = str.end();
+    while (b != e && isspace((unsigned char) e[-1]))
+        --e;
+    return str.fast_substring(b, e);
+}
+
+template <typename T>
+inline typename T::substring_type String_generic::trim(const T &str) {
+    const char *b = str.begin(), *e = str.end();
+    while (b != e && isspace((unsigned char) e[-1]))
+        --e;
+    while (b != e && isspace((unsigned char) b[0]))
+        ++b;
+    return str.fast_substring(b, e);
+}
+
+#if HAVE_STD_HASH
+# define LCDF_MAKE_STRING_HASH(type) \
+    namespace std { template <> struct hash<type>          \
+        : public unary_function<const type&, size_t> {     \
+        size_t operator()(const type& x) const noexcept {  \
+            return x.hashcode();                           \
+        } }; }
+#else
+# define LCDF_MAKE_STRING_HASH(type)
+#endif
+
+template <typename T> template <typename E>
+typename String_base<T>::const_iterator String_base<T>::encode_json_partial(E& enc) const {
+    const char *last = this->begin(), *end = this->end();
+    for (const char *s = last; s != end; ++s) {
+        int c = (unsigned char) *s;
+
+        // U+2028 and U+2029 can't appear in Javascript strings! (Though
+        // they are legal in JSON strings, according to the JSON
+        // definition.)
+        if (unlikely(c == 0xE2)
+            && s + 2 < end && (unsigned char) s[1] == 0x80
+            && (unsigned char) (s[2] | 1) == 0xA9)
+            c = 0x2028 + (s[2] & 1);
+        else if (likely(c >= 32 && c != '\\' && c != '\"' && c != '/'))
+            continue;
+
+        enc.append(last, s);
+        enc << '\\';
+        switch (c) {
+        case '\b':
+            enc << 'b';
+            break;
+        case '\f':
+            enc << 'f';
+            break;
+        case '\n':
+            enc << 'n';
+            break;
+        case '\r':
+            enc << 'r';
+            break;
+        case '\t':
+            enc << 't';
+            break;
+        case '\\':
+        case '\"':
+        case '/':
+            enc << (char) c;
+            break;
+        default: { // c is a control character, 0x2028, or 0x2029
+            char* x = enc.reserve(5);
+            snprintf(x, 5, "u%04X", c);
+            if (c > 255)        // skip rest of encoding of U+202[89]
+                s += 2;
+            break;
+        }
+        }
+        last = s + 1;
+    }
+    return last;
+}
+
+template <typename T> template <typename E>
+inline void String_base<T>::encode_json(E& enc) const {
+    const char* last = encode_json_partial(enc);
+    enc.append(last, end());
+}
+
+template <typename T> template <typename E>
+void String_base<T>::encode_base64(E& enc, bool pad) const {
+    char* out = enc.reserve(((length() + 2) * 4) / 3);
+    const unsigned char* s = this->ubegin(), *end = this->uend();
+    for (; end - s >= 3; s += 3) {
+        unsigned x = (s[0] << 16) | (s[1] << 8) | s[2];
+        *out++ = String_generic::base64_encoding_table[x >> 18];
+        *out++ = String_generic::base64_encoding_table[(x >> 12) & 63];
+        *out++ = String_generic::base64_encoding_table[(x >> 6) & 63];
+        *out++ = String_generic::base64_encoding_table[x & 63];
+    }
+    if (end > s) {
+        unsigned x = s[0] << 16;
+        if (end > s + 1)
+            x |= s[1] << 8;
+        *out++ = String_generic::base64_encoding_table[x >> 18];
+        *out++ = String_generic::base64_encoding_table[(x >> 12) & 63];
+        if (end > s + 1)
+            *out++ = String_generic::base64_encoding_table[(x >> 6) & 63];
+        else if (pad)
+            *out++ = '=';
+        if (pad)
+            *out++ = '=';
+    }
+    enc.set_end(out);
+}
+
+template <typename T> template <typename E>
+bool String_base<T>::decode_base64(E& enc) const {
+    char* out = enc.reserve((length() * 3) / 4 + 1);
+    const unsigned char* s = this->ubegin(), *end = this->uend();
+    while (end > s && end[-1] == '=')
+        --end;
+    for (; end - s >= 4; s += 4) {
+        unsigned x = ((((unsigned) String_generic::base64_decoding_map[s[0]]) - 1) << 18)
+            | ((((unsigned) String_generic::base64_decoding_map[s[1]]) - 1) << 12)
+            | ((((unsigned) String_generic::base64_decoding_map[s[2]]) - 1) << 6)
+            | (((unsigned) String_generic::base64_decoding_map[s[3]]) - 1);
+        if ((int) x < 0)
+            return false;
+        *out++ = (unsigned char) (x >> 16);
+        *out++ = (unsigned char) (x >> 8);
+        *out++ = (unsigned char) x;
+    }
+    if (end - s >= 2) {
+        unsigned x = ((((unsigned) String_generic::base64_decoding_map[s[0]]) - 1) << 18)
+            | ((((unsigned) String_generic::base64_decoding_map[s[1]]) - 1) << 12)
+            | (end - s == 3 ? (((unsigned) String_generic::base64_decoding_map[s[2]]) - 1) << 6 : 0);
+        if ((int) x < 0)
+            return false;
+        *out++ = (unsigned char) (x >> 16);
+        if (end - s == 3)
+            *out++ = (unsigned char) (x >> 8);
+    } else if (end - s)
+        return false;
+    enc.set_end(out);
+    return true;
+}
+
+} // namespace lcdf
+#endif
diff --git a/silo/masstree/string_slice.cc b/silo/masstree/string_slice.cc
new file mode 100644 (file)
index 0000000..2334912
--- /dev/null
@@ -0,0 +1,16 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "string_slice.hh"
diff --git a/silo/masstree/string_slice.hh b/silo/masstree/string_slice.hh
new file mode 100644 (file)
index 0000000..1a4d8f4
--- /dev/null
@@ -0,0 +1,166 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef STRING_SLICE_HH
+#define STRING_SLICE_HH 1
+#include "str.hh"
+#include <algorithm>
+#include <assert.h>
+#include <string.h>
+class threadinfo;
+
+/** @brief Provide access to T-typed slices of a string. */
+template <typename T> struct string_slice {
+  private:
+    union union_type {
+        T x;
+        char s[sizeof(T)];
+        union_type(T x)
+            : x(x) {
+        }
+    };
+
+  public:
+    typedef T type;
+
+    /** @brief Size of T in bytes. */
+    static constexpr int size = (int) sizeof(T);
+
+    /** @brief Return a T containing data from a string's prefix. */
+    static T make(const char *s, int len) {
+        if (len <= 0)
+            return 0;
+#if HAVE_UNALIGNED_ACCESS
+        if (len >= size)
+            return *reinterpret_cast<const T *>(s);
+#endif
+        union_type u(0);
+        memcpy(u.s, s, std::min(len, size));
+        return u.x;
+    }
+
+    /** @brief Return a T that compares similarly to a string's prefix.
+
+        If a = make_comparable(s1, l1), b = make_comparable(s2, l2), and
+        a < b, then the string (s1, l1) is lexicographically less than
+        the string (s2, l2). Similarly, if a > b, then (s1, l1) is
+        lexicographically greater than (s2, l2). If a == b, then the
+        prefixes of (s1, l1) and (s2, l2) are lexicographically equal. */
+    static T make_comparable(const char *s, int len) {
+        return net_to_host_order(make(s, len));
+    }
+
+    /** @brief Return a T containing data from a string's prefix.
+        @pre It is safe to access characters in the range
+          [@a s - size - 1, @a s + size).
+
+        This function acts like make(), but can use single memory accesses for
+        short strings. These accesses may observe data outside the range [@a
+        s, @a s + len). */
+    static T make_sloppy(const char *s, int len) {
+        if (len <= 0)
+            return 0;
+#if HAVE_UNALIGNED_ACCESS
+        if (len >= size)
+            return *reinterpret_cast<const T *>(s);
+# if WORDS_BIGENDIAN
+        return *reinterpret_cast<const T *>(s) & (~T(0) << (8 * (size - len)));
+# elif WORDS_BIGENDIAN_SET
+        return *reinterpret_cast<const T *>(s - (size - len)) >> (8 * (size - len));
+# else
+#  error "WORDS_BIGENDIAN has not been set!"
+# endif
+#else
+        union_type u(0);
+        memcpy(u.s, s, std::min(len, size));
+        return u.x;
+#endif
+    }
+
+    /** @brief Return a T that compares similarly to a string's prefix.
+        @pre It is safe to access characters in the range
+          [@a s - size - 1, @a s + size).
+
+        This function acts like make_comparable(), but can use single memory
+        accesses for short strings. These accesses may observe data outside
+        the range [@a s, @a s + len). */
+    static T make_comparable_sloppy(const char *s, int len) {
+        return net_to_host_order(make_sloppy(s, len));
+    }
+
+
+    /** @brief Unparse a comparable @a value into a buffer.
+        @return Number of characters unparsed (<= buflen).
+
+        If @a value was created by string_slice::make_comparable(s, x), then
+        after this function returns, @a buf contains a string equal to the
+        original @a s, except that trailing null characters have been
+        removed. */
+    static int unparse_comparable(char *buf, int buflen, T value) {
+        union_type u(host_to_net_order(value));
+        int l = size;
+        while (l > 0 && u.s[l - 1] == 0)
+            --l;
+        l = std::min(l, buflen);
+        memcpy(buf, u.s, l);
+        return l;
+    }
+
+    /** @brief Unparse a comparable @a value into a buffer.
+        @return Number of characters unparsed (<= buflen).
+
+        If @a value was created by string_slice::make_comparable(s, @a len),
+        then after this function returns, @a buf contains a string equal to
+        the first @a len bytes of s. */
+    static int unparse_comparable(char *buf, int buflen, T value, int len) {
+        union_type u(host_to_net_order(value));
+        int l = std::min(std::min(len, size), buflen);
+        memcpy(buf, u.s, l);
+        return l;
+    }
+
+
+    /** @brief Test two strings for equality.
+        @param a first string
+        @param b second string
+        @param len number of characters in @a a and @a b
+        @return true iff the two strings' first @a len characters are equal
+        @pre It is safe to access characters in the ranges
+          [@a a - size + 1, @a a + size) and [@a b - size + 1, @a b + size).
+
+        Always returns the same result as "memcmp(@a a, @a b, @a len) == 0",
+        but can be faster on some machines. */
+    static bool equals_sloppy(const char *a, const char *b, int len) {
+#if HAVE_UNALIGNED_ACCESS
+        if (len <= size) {
+            typename mass::make_unsigned<T>::type delta
+                = *reinterpret_cast<const T *>(a)
+                ^ *reinterpret_cast<const T *>(b);
+            if (unlikely(len <= 0))
+                return true;
+# if WORDS_BIGENDIAN
+            return (delta >> (8 * (size - len))) == 0;
+# else
+            return (delta << (8 * (size - len))) == 0;
+# endif
+        }
+#endif
+        return memcmp(a, b, len) == 0;
+    }
+};
+
+template <typename T> constexpr int string_slice<T>::size;
+
+#endif
diff --git a/silo/masstree/stringbag.hh b/silo/masstree/stringbag.hh
new file mode 100644 (file)
index 0000000..701de14
--- /dev/null
@@ -0,0 +1,162 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef STRINGBAG_HH
+#define STRINGBAG_HH 1
+#include "compiler.hh"
+#include "string_slice.hh"
+
+/** @brief String collection used for Masstree key suffixes.
+
+    A stringbag is a compact collection of up to W strings, where W is a
+    parameter called the <em>bag width</em>. These strings are stored
+    in contiguously allocated memory.
+
+    Stringbag component strings support
+    string_slice<uintptr_t>::equals_sloppy() without memory errors.
+
+    The template parameter T is the offset type. This type determines the
+    maximum supported capacity of a stringbag. Smaller types have lower
+    overhead, but support smaller bags.
+
+    Stringbags favor compactness over usability. The bag width W is an
+    important parameter, but you can't recover W from the stringbag itself;
+    you'll need to store that externally. Stringbags cannot be allocated
+    conventionally. You must manage stringbag memory yourself:
+    allocate an array of characters for the stringbag, then use placement
+    new to construct the stringbag on that memory. Stringbag has a
+    trivial destructor. */
+template <typename T>
+class stringbag {
+ public:
+    /** @brief Type of offsets. */
+    typedef T offset_type;
+    typedef string_slice<uintptr_t> slice_type;
+
+ private:
+    struct info_type {
+        offset_type pos;
+        offset_type len;
+        info_type(unsigned p, unsigned l)
+            : pos(p), len(l) {
+        }
+    };
+
+ public:
+    /** @brief Return the maximum allowed capacity of a stringbag. */
+    static constexpr unsigned max_size() {
+        return ((unsigned) (offset_type) -1) + 1;
+    }
+    /** @brief Return the overhead for a stringbag of width @a width.
+
+        This is the number of bytes allocated for overhead. */
+    static constexpr size_t overhead(int width) {
+        return sizeof(stringbag<T>) + width * sizeof(info_type);
+    }
+    /** @brief Return a capacity that can definitely contain a stringbag.
+        @param width number of strings in bag
+        @param len total number of bytes in bag's strings */
+    static constexpr size_t safe_size(int width, unsigned len) {
+        return overhead(width) + len + slice_type::size - 1;
+    }
+
+    /** @brief Construct an empty stringbag.
+        @param width Number of strings in the bag
+        @param capacity Number of bytes allocated for the bag
+        @pre @a capacity > overhead(width)
+        @pre @a capacity <= max_offset
+
+        Stringbags should not be constructed on the stack or by a direct call
+        to new. Allocate space for a stringbag, then call the constructor on
+        that space using placement new. @a capacity must be no bigger than
+        the allocated space. */
+    stringbag(int width, size_t capacity) {
+        size_t firstpos = overhead(width);
+        assert(capacity >= firstpos && capacity <= max_size());
+        size_ = firstpos;
+        capacity_ = capacity - 1;
+        memset(info_, 0, sizeof(info_type) * width);
+    }
+
+    /** @brief Return the capacity used to construct this bag. */
+    size_t capacity() const {
+        return capacity_ + 1;
+    }
+    /** @brief Return the number of bytes used so far (including overhead). */
+    size_t used_capacity() const {
+        return size_;
+    }
+
+    /** @brief Return the string at position @a p.
+        @pre @a p >= 0 && @a p < bag width */
+    lcdf::Str operator[](int p) const {
+        info_type info = info_[p];
+        return lcdf::Str(s_ + info.pos, info.len);
+    }
+    /** @brief Return the string at position @a p.
+        @pre @a p >= 0 && @a p < bag width */
+    lcdf::Str get(int p) const {
+        info_type info = info_[p];
+        return lcdf::Str(s_ + info.pos, info.len);
+    }
+
+    /** @brief Assign the string at position @a p to @a s.
+        @param p position
+        @param s string
+        @param len length of string
+        @return true if the assignment succeeded, false if it failed
+           (because the stringbag is out of capacity)
+        @pre @a p >= 0 && @a p < bag width */
+    bool assign(int p, const char *s, int len) {
+        unsigned pos, mylen = info_[p].len;
+        if (mylen >= (unsigned) len)
+            pos = info_[p].pos;
+        else if (size_ + (unsigned) std::max(len, slice_type::size)
+                   <= capacity()) {
+            pos = size_;
+            size_ += len;
+        } else
+            return false;
+        memcpy(s_ + pos, s, len);
+        info_[p] = info_type(pos, len);
+        return true;
+    }
+    /** @override */
+    bool assign(int p, lcdf::Str s) {
+        return assign(p, s.s, s.len);
+    }
+
+    /** @brief Print a representation of the stringbag to @a f. */
+    void print(int width, FILE *f, const char *prefix, int indent) {
+        fprintf(f, "%s%*s%p (%d:)%d:%d...\n", prefix, indent, "",
+                this, (int) overhead(width), size_, capacity());
+        for (int i = 0; i < width; ++i)
+            if (info_[i].len)
+                fprintf(f, "%s%*s  #%x %u:%u %.*s\n", prefix, indent, "",
+                        i, info_[i].pos, info_[i].len, std::min(info_[i].len, 40U), s_ + info_[i].pos);
+    }
+
+  private:
+    union {
+        struct {
+            offset_type size_;
+            offset_type capacity_;
+            info_type info_[0];
+        };
+        char s_[0];
+    };
+};
+
+#endif
diff --git a/silo/masstree/test_atomics.cc b/silo/masstree/test_atomics.cc
new file mode 100644 (file)
index 0000000..634ad76
--- /dev/null
@@ -0,0 +1,468 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#define FORCE_ENABLE_ASSERTIONS 1
+#undef NDEBUG
+#include "compiler.hh"
+#include <stdlib.h>
+#include <algorithm>
+#include <sys/time.h>
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include "kvrandom.hh"
+#include "string_slice.hh"
+#include "kpermuter.hh"
+#include "value_bag.hh"
+#include "value_string.hh"
+#include "json.hh"
+using namespace lcdf;
+
+uint8_t xb[100];
+uint16_t xw[100];
+uint32_t xl[100];
+
+struct fake_threadinfo {
+    static void* allocate(size_t sz, memtag = memtag_none) {
+       return new char[sz];
+    }
+    static void deallocate(void* p, size_t, memtag = memtag_none) {
+       delete[] reinterpret_cast<char*>(p);
+    }
+};
+
+// also check bitfield layout
+union myunion {
+    uint32_t x;
+    struct {
+       unsigned one : 1;
+       unsigned two : 2;
+       unsigned three : 3;
+    };
+} xunion;
+
+void test_atomics() {
+    uint32_t x;
+
+    xunion.one = 1;
+    assert(xunion.x == 1);
+
+    x = xchg(&xb[0], 2);
+    assert(x == 0 && xb[0] == 2 && xb[1] == 0 && xb[2] == 0 && xb[3] == 0);
+    x = xchg(&xb[0], 3);
+    assert(x == 2 && xb[0] == 3 && xb[1] == 0 && xb[2] == 0 && xb[3] == 0);
+
+    x = xchg(&xw[0], 1);
+    assert(x == 0);
+
+    x = fetch_and_add(&xl[0], 100);
+    assert(x == 0 && xl[0] == 100);
+    x = cmpxchg(&xl[0], 100, 200);
+    assert(x == 100 && xl[0] == 200);
+    if (bool_cmpxchg(&xl[0], 200, 300))
+       xl[1] = 400;
+    assert(xl[0] == 300 && xl[1] == 400);
+    if (bool_cmpxchg(&xl[0], 400, 100))
+       xl[1] = 500;
+    assert(xl[0] == 300 && xl[1] == 400);
+}
+
+void test_psdes_nr() {
+    kvrandom_psdes_nr r;
+    union {
+       uint32_t u;
+       float f;
+    } u;
+
+    r.reset(1);
+    u.u = r.next();
+    assert(u.u == 0x509C0C23U);
+    u.u = r[99];
+    assert(u.u == 0xA66CB41A);
+
+    r.reset(99);
+    u.u = r.next();
+    assert(u.u == 0x64300984);
+    u.u = r[99];
+    assert(u.u == 0x59BA89EB);
+}
+
+template <typename T>
+void time_random() {
+    T r;
+    uint32_t x = 0;
+    for (int i = 0; i < 1000000000; ++i)
+       x ^= r.next();
+    assert(x != 0);
+}
+
+template <typename T>
+void time_keyslice() {
+    char *b = (char *) malloc(4096);
+    FILE *f = fopen("/dev/urandom", "rb");
+    ssize_t rr = fread(b, 1, 4096, f);
+    assert(rr == 4096);
+    fclose(f);
+    T x = 0;
+    kvrandom_lcg_nr r;
+    x ^= string_slice<T>::make(b, 1);
+    for (int i = 0; i < 1000000000; ++i)
+       x ^= string_slice<T>::make(b + r.next() % 2048, r.next() % 16);
+    assert(x);
+}
+
+void test_kpermuter() {
+    typedef kpermuter<15> kpermuter_type;
+    kpermuter_type k = kpermuter_type::make_empty();
+    int i = k.insert_from_back(0);
+    assert(k.size() == 1 && i == 0 && k[0] == 0);
+    i = k.insert_from_back(0);
+    assert(k.size() == 2 && i == 1 && k[0] == 1 && k[1] == 0);
+    i = k.insert_from_back(2);
+    assert(k.size() == 3 && i == 2 && k[0] == 1 && k[1] == 0 && k[2] == 2);
+    k.remove(1);
+    assert(k.size() == 2 && k[0] == 1 && k[1] == 2 && k[2] == 0);
+    k.remove(1);
+    assert(k.size() == 1 && k[0] == 1 && k[1] == 2 && k[2] == 0 && k[3] == 14);
+    i = k.insert_from_back(0);
+    assert(k.size() == 2 && i == 3 && k[0] == 3 && k[1] == 1 && k[2] == 2 && k[3] == 0 && k[4] == 14);
+    k.insert_selected(0, 3);
+    assert(k.size() == 3 && k[0] == 0 && k[1] == 3 && k[2] == 1 && k[3] == 2 && k[4] == 14);
+    k.insert_selected(2, 11);
+    assert(k.size() == 4 && k[0] == 0 && k[1] == 3 && k[2] == 7 && k[3] == 1 && k[4] == 2
+          && k[5] == 14 && k[11] == 8 && k[12] == 6);
+    k.insert_selected(2, 14);
+    assert(k.size() == 5 && k[0] == 0 && k[1] == 3 && k[2] == 4 && k[3] == 7 && k[4] == 1 && k[5] == 2
+          && k[6] == 14 && k[12] == 8 && k[13] == 6);
+    k.exchange(0, 1);
+    assert(k.size() == 5 && k[0] == 3 && k[1] == 0 && k[2] == 4 && k[3] == 7 && k[4] == 1 && k[5] == 2
+          && k[6] == 14 && k[12] == 8 && k[13] == 6);
+    k.remove_to_back(2);
+    assert(k.size() == 4 && k[0] == 3 && k[1] == 0 && k[2] == 7 && k[3] == 1 && k[4] == 2
+          && k[5] == 14 && k[11] == 8 && k[12] == 6 && k[14] == 4);
+    assert(k.back() == 4);
+    i = k.insert_from_back(2);
+    assert(k.size() == 5 && k[0] == 3 && k[1] == 0 && k[2] == 4 && k[3] == 7 && k[4] == 1 && k[5] == 2
+          && k[6] == 14 && k[12] == 8 && k[13] == 6 && i == 4);
+    k.exchange(0, 0);
+    assert(k.size() == 5 && k[0] == 3 && k[1] == 0 && k[2] == 4 && k[3] == 7 && k[4] == 1 && k[5] == 2
+          && k[6] == 14 && k[12] == 8 && k[13] == 6 && i == 4);
+
+    assert(find_lowest_zero_nibble(0x0120U) == 0);
+    assert(find_lowest_zero_nibble(0x0123U) == 3);
+    assert(find_lowest_zero_nibble(0x12345678U) == -1);
+
+    kpermuter<14> ka = kpermuter<14>::make_empty();
+    i = ka.insert_from_back(0);
+    assert(ka.size() == 1 && i == 0 && ka[0] == 0);
+    i = ka.insert_from_back(0);
+    assert(ka.size() == 2 && i == 1 && ka[0] == 1 && ka[1] == 0);
+    i = ka.insert_from_back(2);
+    assert(ka.size() == 3 && i == 2 && ka[0] == 1 && ka[1] == 0 && ka[2] == 2);
+    ka.remove_to_back(1);
+    assert(ka.size() == 2 && ka[0] == 1 && ka[1] == 2 && ka.back() == 0);
+}
+
+void test_string_slice() {
+    typedef string_slice<uint32_t> ss_type;
+    assert(ss_type::make("a", 1) == ss_type::make("aaa", 1));
+    assert(ss_type::make_sloppy("0123abcdef" + 4, 1)
+          == ss_type::make_sloppy("bcdea01293" + 4, 1));
+    assert(ss_type::make_comparable("a", 1) < ss_type::make_comparable("b", 1));
+    assert(ss_type::equals_sloppy("0123abcdef" + 4, "abcdea02345" + 5, 1));
+    assert(ss_type::make_comparable("abcd", 4) < ss_type::make_comparable("abce", 4));
+    assert(ss_type::make_comparable("abce", 4) > ss_type::make_comparable("abcd", 4));
+    assert(ss_type::equals_sloppy("0123abcdef" + 4, "abcdeabcd5" + 5, 4));
+    assert(!ss_type::equals_sloppy("0123abcdef" + 4, "abcdeabcd5" + 5, 5));
+    assert(String("12345").find_right("") == 5);
+    assert(String("12345").find_right("5") == 4);
+    assert(String("12345").find_right("23") == 1);
+    assert(String("12345", 0).find_right("23") == -1);
+}
+
+void test_string_bag() {
+    fake_threadinfo ti;
+    typedef value_bag<uint16_t> bag_t;
+    bag_t eb;
+    if (eb.size() > sizeof(bag_t))
+       fprintf(stderr, "sizes are off: %zu vs. %zu\n", eb.size(), sizeof(bag_t));
+    assert(eb.size() <= sizeof(bag_t));
+    bag_t* b = eb.update(0, Str("A", 1), 1, ti);
+    assert(b->row_string() == Str("\001\000\006\000\007\000A", 7));
+    assert(b->ncol() == 1);
+    assert(b->col(0) == Str("A", 1));
+
+    bag_t* bx = bag_t::create1(Str("A", 1), 1, ti);
+    assert(bx->row_string() == b->row_string());
+    bx->deallocate(ti);
+
+    bag_t *bb = b->update(1, Str("BB", 2), 2, ti);
+    b->deallocate(ti);
+    b = bb;
+    assert(b->row_string() == Str("\002\000"
+                                 "\010\000\011\000\013\000"
+                                 "ABB", 013));
+    assert(b->ncol() == 2);
+    assert(b->col(0) == Str("A", 1));
+    assert(b->col(1) == Str("BB", 2));
+
+    bb = b->update(3, Str("CCC", 3), 3, ti);
+    b->deallocate(ti);
+    b = bb;
+    assert(b->row_string() == Str("\004\000"
+                                 "\014\000\015\000\017\000\017\000\022\000"
+                                 "ABBCCC", 022));
+    assert(b->ncol() == 4);
+    assert(b->col(0) == Str("A", 1));
+    assert(b->col(1) == Str("BB", 2));
+    assert(b->col(2) == Str("", 0));
+    assert(b->col(3) == Str("CCC", 3));
+
+    bb = b->update(1, Str("bbb", 3), 4, ti);
+    b->deallocate(ti);
+    b = bb;
+    assert(b->row_string() == Str("\004\000"
+                                 "\014\000\015\000\020\000\020\000\023\000"
+                                 "AbbbCCC", 023));
+    assert(b->ncol() == 4);
+    assert(b->col(0) == Str("A", 1));
+    assert(b->col(1) == Str("bbb", 3));
+    assert(b->col(2) == Str("", 0));
+    assert(b->col(3) == Str("CCC", 3));
+
+    bb = b->update(0, Str("a", 1), 4, ti);
+    b->deallocate(ti);
+    b = bb;
+    assert(b->row_string() == Str("\004\000"
+                                 "\014\000\015\000\020\000\020\000\023\000"
+                                 "abbbCCC", 023));
+    assert(b->ncol() == 4);
+    assert(b->col(0) == Str("a", 1));
+    assert(b->col(1) == Str("bbb", 3));
+    assert(b->col(2) == Str("", 0));
+    assert(b->col(3) == Str("CCC", 3));
+
+    bb = b->update(1, Str("", 0), 4, ti);
+    b->deallocate(ti);
+    b = bb;
+    assert(b->row_string() == Str("\004\000"
+                                 "\014\000\015\000\015\000\015\000\020\000"
+                                 "aCCC", 020));
+    assert(b->ncol() == 4);
+    assert(b->col(0) == Str("a", 1));
+    assert(b->col(1) == Str("", 0));
+    assert(b->col(2) == Str("", 0));
+    assert(b->col(3) == Str("CCC", 3));
+
+    b->deallocate(ti);
+}
+
+void test_json()
+{
+    Json j;
+    assert(j.empty());
+    assert(!j);
+
+    j = Json::make_object();
+    assert(j.empty());
+    assert(j);
+
+    j.set("foo", "bar");
+    assert(j["foo"]);
+    assert(j["foo"].to_s() == "bar");
+    assert(j.size() == 1);
+
+    j.set("baz", "flim");
+    assert(j.size() == 2);
+    assert(j.unparse() == "{\"foo\":\"bar\",\"baz\":\"flim\"}");
+
+    j.erase("foo");
+    assert(j.size() == 1);
+
+    j.assign_parse("2");
+    assert(j == 2);
+
+    j.assign_parse("null");
+    assert(j == Json());
+
+    j.assign_parse("\"a\"");
+    assert(j == "a");
+
+    j.assign_parse("[1,2]");
+    assert(j.unparse() == "[1,2]");
+
+    j.assign_parse("[[[]],{\"a\":{}}]");
+    assert(j.unparse() == "[[[]],{\"a\":{}}]");
+
+    j = Json::parse("{\"x22\":{\n\
+      \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
+      \"time\":\"Tue Feb  7 20:20:33 2012\",\n\
+      \"machine\":\"rtshanks-laptop\",\n\
+      \"cores\":2,\n\
+      \"runs\":[\"x22\\/rw2\\/mb\\/0\"]\n\
+    },\n\
+    \"x23\":{\n\
+      \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
+      \"time\":\"Tue Feb  7 20:31:05 2012\",\n\
+      \"machine\":\"rtshanks-laptop\",\n\
+      \"cores\":2,\n\
+      \"runs\":[\"x23\\/rw2\\/mb\\/0\",\"x23\\/rw1\\/mb\\/0\",\"x23\\/rw3\\/mb\\/0\"]\n\
+    },\n\
+    \"x24\":{\n\
+      \"git-revision\":\"62e9970ca8ae9c6eebf2d71b7065ea694fb25282M\",\n\
+      \"time\":\"Sat Feb 11 15:54:01 2012\",\n\
+      \"machine\":\"rtshanks-laptop\",\n\
+      \"cores\":2,\n\
+      \"runs\":[\"x24\\/rw1\\/b\\/0\"]\n\
+    },\"b\":\"c\",\"c\":\"d\"}");
+    assert(j["x22"]["time"] == "Tue Feb  7 20:20:33 2012");
+    assert(j["x22"]["cores"] == 2);
+    {
+       Json::object_iterator it = j.obegin();
+       assert(it.key() == "x22");
+       ++it;
+       assert(it.key() == "x23");
+       ++it;
+       assert(it.key() == "x24");
+       ++it;
+       assert(it.key() == "b");
+       ++it;
+       assert(it.key() == "c");
+    }
+
+    {
+       Json jcopy = j;
+       assert(j.size() == 5);
+       int count = 0;
+       for (Json::iterator it = jcopy.begin(); it != jcopy.end(); ++it) {
+           it->second = Json();
+           ++count;
+       }
+       assert(!jcopy["x22"]);
+       assert(j["x22"]["cores"] == 2);
+       assert(count == jcopy.size());
+    }
+
+    assert(!j["x49"]);
+    assert(j.size() == 5);
+    assert(!j["x49"]["45"][2]["a"]);
+    assert(j.size() == 5);
+    j["x49"]["45"][2]["a"] = 1;
+    assert(j.size() == 6);
+    assert(j["x22"].is_object() && j.get("x22").is_object());
+    assert(j["x23"].is_object() && j.get("x23").is_object());
+    assert(j["b"].is_string() && j.get("b").is_string());
+    assert(j["x49"].is_object() && j.get("x49").is_object());
+    assert(j["x49"]["45"].is_array());
+    assert(j["x49"]["45"].size() == 3 && j["x49"]["45"][2].is_object());
+
+    j = Json::make_object();
+    j["a"] = j["b"];
+    assert(j.size() == 1);
+    assert(j["a"].is_null());
+    assert(j.count("a") == 1);
+
+    j = Json::make_object();
+    Json k = Json::make_array();
+    j["a"] = k[2];
+    assert(j.size() == 1);
+    assert(k.size() == 0);
+    assert(j["a"].is_null());
+    assert(j.count("a") == 1);
+
+    j = Json(1);
+    assert(j.get("a").is_null());
+
+    j.assign_parse("{\"a\":1,\"b\":true,\"c\":\"\"}");
+    {
+       int i = 0;
+       bool b = false;
+       String s;
+       assert(j.get("a", i));
+       assert(i == 1);
+       assert(j.get("a", i).get("b", b));
+       assert(b == true);
+       assert(!j.get("a", s).status());
+       assert(!j.get("a", s).status(b));
+       assert(b == false);
+       assert(j.get("a", k));
+       assert(k == Json(1));
+       assert(!j.get("cc", k));
+    }
+
+    j["a"] = Json(5);
+    j.set("a", Json(5));
+    j["b"] = Json::parse("[]");
+}
+
+void test_value_updates() {
+    fake_threadinfo ti;
+    typedef value_bag<uint16_t> bag_t;
+    typedef value_string vstr_t;
+    bag_t* eb = new(ti.allocate(sizeof(bag_t))) bag_t;
+    vstr_t* strb = new(ti.allocate(sizeof(vstr_t))) vstr_t;
+
+    Json bagupdate = Json::array(0, "ABC", 1, "def", 2, "EGHIJ", 3, "klm");
+
+    Json strupdate = Json::array(vstr_t::make_index(0, 3), "ABC",
+                                 vstr_t::make_index(3, 3), "def",
+                                 vstr_t::make_index(6, 5), "EGHIJ",
+                                 vstr_t::make_index(11, 3), "klm");
+
+    {
+        bag_t* eb2 = eb->update(bagupdate.array_data(), bagupdate.end_array_data(), 1, ti);
+        eb->deallocate(ti);
+        eb = eb2;
+        assert(eb->col(0) == Str("ABC"));
+        assert(eb->col(1) == Str("def"));
+        assert(eb->col(2) == Str("EGHIJ"));
+        assert(eb->col(3) == Str("klm"));
+    }
+
+    {
+        vstr_t* strb2 = strb->update(strupdate.array_data(), strupdate.end_array_data(), 1, ti);
+        strb->deallocate(ti);
+        strb = strb2;
+        assert(strb->col(vstr_t::make_index(0, 3)) == Str("ABC"));
+        assert(strb->col(vstr_t::make_index(3, 3)) == Str("def"));
+        assert(strb->col(vstr_t::make_index(6, 5)) == Str("EGHIJ"));
+        assert(strb->col(vstr_t::make_index(11, 3)) == Str("klm"));
+        assert(strb->col(0) == Str("ABCdefEGHIJklm"));
+    }
+
+    eb->deallocate(ti);
+    strb->deallocate(ti);
+}
+
+int main(int, char *[])
+{
+    //test_atomics();
+    //test_psdes_nr();
+    //time_random<kvrandom_lcg_nr>();
+    assert(iceil_log2(2) == 2);
+    assert(iceil_log2(3) == 4);
+    assert(ifloor_log2(2) == 2);
+    assert(ifloor_log2(3) == 2);
+    //time_keyslice<uint64_t>();
+    test_kpermuter();
+    test_string_slice();
+    test_string_bag();
+    test_json();
+    test_value_updates();
+    std::cout << "Tests complete!\n";
+    return 0;
+}
diff --git a/silo/masstree/test_string.cc b/silo/masstree/test_string.cc
new file mode 100644 (file)
index 0000000..8ad17a4
--- /dev/null
@@ -0,0 +1,89 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "string.hh"
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include "straccum.hh"
+
+template <typename T>
+static bool
+check_straccum_utf8(StringAccum &sa, const char *in, int inlen,
+                   const char *out, int outlen)
+{
+    sa.clear();
+    Encoding::UTF8Encoder<T> encoder;
+    sa.append_encoded(encoder, in, in + inlen);
+    return sa.length() == outlen && memcmp(sa.begin(), out, sa.length()) == 0;
+}
+
+template <typename T>
+static bool
+check_straccum2_utf8(StringAccum &sa, const char *in, int inlen,
+                    const char *out, int outlen)
+{
+    sa.clear();
+    memcpy(sa.reserve(inlen), in, inlen);
+    Encoding::UTF8Encoder<T> encoder;
+    sa.append_encoded(encoder, sa.begin(), sa.begin() + inlen);
+    return sa.length() == outlen && memcmp(sa.begin(), out, sa.length()) == 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+    assert(String("abc").to_utf8() == "abc");
+    assert(String("").to_utf8() == "");
+    assert(String("ab\000cd", 5).to_utf8() == "abcd");
+    assert(String("\xc3\x9dHi!").to_utf8() == "\xc3\x9dHi!");
+    assert(String("\xddHi!").to_utf8() == "\xc3\x9dHi!");
+    assert(String("\xc3\x9dHi!\x9c").to_utf8() == "\xc3\x9dHi!\xc5\x93");
+    assert(String("ab\000c\x9c", 5).to_utf8() == "abc\xc5\x93");
+    assert(String("\xc3\x9dXY\000c\x9c", 7).to_utf8() == "\xc3\x9dXYc\xc5\x93");
+
+    StringAccum sa;
+    check_straccum_utf8<Encoding::UTF8>(sa, "abc", 3, "abc", 3);
+    check_straccum_utf8<Encoding::UTF8>(sa, "", 0, "", 0);
+    check_straccum_utf8<Encoding::UTF8>(sa, "ab\000cd", 5, "ab\000cd", 5);
+    check_straccum_utf8<Encoding::UTF8NoNul>(sa, "ab\000cd", 5, "abcd", 4);
+    check_straccum_utf8<Encoding::UTF8>(sa, "\xc3\x9dHi!", 5, "\xc3\x9dHi!", 5);
+    check_straccum_utf8<Encoding::Windows1252>(sa, "\xddHi!", 4, "\xc3\x9dHi!", 5);
+
+    check_straccum2_utf8<Encoding::UTF8>(sa, "abc", 3, "abc", 3);
+    check_straccum2_utf8<Encoding::UTF8>(sa, "", 0, "", 0);
+    check_straccum2_utf8<Encoding::UTF8>(sa, "ab\000cd", 5, "ab\000cd", 5);
+    check_straccum2_utf8<Encoding::UTF8NoNul>(sa, "ab\000cd", 5, "abcd", 4);
+    check_straccum2_utf8<Encoding::UTF8>(sa, "\xc3\x9dHi!", 5, "\xc3\x9dHi!", 5);
+    check_straccum2_utf8<Encoding::Windows1252>(sa, "\xddHi!", 4, "\xc3\x9dHi!", 5);
+
+    if (argc == 2) {
+       FILE *f;
+       if (strcmp(argv[1], "-") == 0)
+           f = stdin;
+       else if (!(f = fopen(argv[1], "rb"))) {
+           perror("test_string");
+           exit(1);
+       }
+       StringAccum sa;
+       while (!feof(f)) {
+           size_t x = fread(sa.reserve(1024), 1, 1024, f);
+           sa.adjust_length(x);
+       }
+       String s = sa.take_string().to_utf8(String::utf_strip_bom);
+       fwrite(s.data(), 1, s.length(), stdout);
+    }
+}
diff --git a/silo/masstree/testrunner.cc b/silo/masstree/testrunner.cc
new file mode 100644 (file)
index 0000000..c369093
--- /dev/null
@@ -0,0 +1,54 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "testrunner.hh"
+#include <algorithm>
+#include <numeric>
+#include <vector>
+
+testrunner_base* testrunner_base::thehead;
+testrunner_base* testrunner_base::thetail;
+
+void testrunner_base::print_names(FILE* stream, int ncol) {
+    masstree_precondition(ncol >= 1);
+
+    std::vector<lcdf::String> names;
+    for (testrunner_base* tr = thehead; tr; tr = tr->next_)
+        names.push_back(tr->name());
+
+    size_t percol;
+    std::vector<int> colwidth;
+    while (1) {
+        percol = (names.size() + ncol - 1) / ncol;
+        colwidth.assign(ncol, 0);
+        for (size_t i = 0; i != names.size(); ++i)
+            colwidth[i/percol] = std::max(colwidth[i/percol], names[i].length());
+        if (ncol == 1
+            || std::accumulate(colwidth.begin(), colwidth.end(), 0)
+               + ncol * 3 <= 78)
+            break;
+        --ncol;
+    }
+
+    for (size_t row = 0; row != percol; ++row) {
+        size_t off = row;
+        for (int col = 0; col != ncol; ++col, off += percol)
+            if (off < names.size())
+                fprintf(stream, "%*s   %s",
+                        col ? colwidth[col-1] - names[off-percol].length() : 0, "",
+                        names[off].c_str());
+        fprintf(stream, "\n");
+    }
+}
diff --git a/silo/masstree/testrunner.hh b/silo/masstree/testrunner.hh
new file mode 100644 (file)
index 0000000..1afd012
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef MASSTREE_TESTRUNNER_HH
+#define MASSTREE_TESTRUNNER_HH
+#include "string.hh"
+#include <stdio.h>
+
+class testrunner_base {
+  public:
+    testrunner_base(const lcdf::String& name)
+        : name_(name), next_(0) {
+        thehead ? thetail->next_ = this : thehead = this;
+        thetail = this;
+    }
+    virtual ~testrunner_base() {
+    }
+    const lcdf::String& name() const {
+        return name_;
+    }
+    static testrunner_base* first() {
+        return thehead;
+    }
+    static testrunner_base* find(const lcdf::String& name) {
+        testrunner_base* t = thehead;
+        while (t && t->name_ != name)
+            t = t->next_;
+        return t;
+    }
+    static void print_names(FILE* stream, int maxcol);
+  private:
+    static testrunner_base* thehead;
+    static testrunner_base* thetail;
+    lcdf::String name_;
+    testrunner_base* next_;
+};
+
+#ifdef TESTRUNNER_CLIENT_TYPE
+
+class testrunner : public testrunner_base {
+  public:
+    inline testrunner(const lcdf::String& name)
+        : testrunner_base(name) {
+    }
+    static testrunner* first() {
+        return static_cast<testrunner*>(testrunner_base::first());
+    }
+    static testrunner* find(const lcdf::String& name) {
+        return static_cast<testrunner*>(testrunner_base::find(name));
+    }
+    virtual void run(TESTRUNNER_CLIENT_TYPE) = 0;
+};
+
+#define MAKE_TESTRUNNER(name, text)                    \
+    namespace {                                        \
+    class testrunner_##name : public testrunner {      \
+    public:                                            \
+        testrunner_##name() : testrunner(#name) {}     \
+        void run(TESTRUNNER_CLIENT_TYPE client) { text; client.finish(); } \
+    }; static testrunner_##name testrunner_##name##_instance; }
+
+#endif
+#endif
diff --git a/silo/masstree/timestamp.hh b/silo/masstree/timestamp.hh
new file mode 100644 (file)
index 0000000..48dc55b
--- /dev/null
@@ -0,0 +1,63 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef TIMESTAMP_HH
+#define TIMESTAMP_HH
+#include "compiler.hh"
+#include <time.h>
+#include <sys/time.h>
+#include <math.h>
+
+#if HAVE_INT64_T_IS_LONG_LONG
+#define PRIuKVTS "llu"
+#else
+#define PRIuKVTS "lu"
+#endif
+#define PRIKVTSPARTS "%lu.%06lu"
+
+#define KVTS_HIGHPART(t) ((unsigned long) ((t) >> 32))
+#define KVTS_LOWPART(t) ((unsigned long) (uint32_t) (t))
+
+typedef uint64_t kvtimestamp_t;
+
+inline kvtimestamp_t timestamp() {
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return ((kvtimestamp_t) tv.tv_sec << 32) | (unsigned int)tv.tv_usec;
+}
+
+inline kvtimestamp_t timestamp_sub(kvtimestamp_t a, kvtimestamp_t b) {
+    a -= b;
+    if (KVTS_LOWPART(a) > 999999)
+       a -= ((kvtimestamp_t) 1 << 32) - 1000000;
+    return a;
+}
+
+extern kvtimestamp_t initial_timestamp;
+
+inline double now() {
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return tv.tv_sec + tv.tv_usec / 1000000.0;
+}
+
+inline struct timespec &set_timespec(struct timespec &x, double y) {
+    double ipart = floor(y);
+    x.tv_sec = (long) ipart;
+    x.tv_nsec = (long) ((y - ipart) * 1e9);
+    return x;
+}
+
+#endif
diff --git a/silo/masstree/value_array.cc b/silo/masstree/value_array.cc
new file mode 100644 (file)
index 0000000..879f0d1
--- /dev/null
@@ -0,0 +1,66 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "kvrow.hh"
+#include "value_array.hh"
+#include <string.h>
+
+value_array* value_array::make_sized_row(int ncol, kvtimestamp_t ts,
+                                         threadinfo& ti) {
+    value_array *tv;
+    tv = (value_array *) ti.allocate(shallow_size(ncol), memtag_value);
+    tv->ts_ = ts;
+    tv->ncol_ = ncol;
+    memset(tv->cols_, 0, sizeof(tv->cols_[0]) * ncol);
+    return tv;
+}
+
+value_array* value_array::update(const Json* first, const Json* last,
+                                 kvtimestamp_t ts, threadinfo& ti) const {
+    masstree_precondition(ts >= ts_);
+    unsigned ncol = std::max(int(ncol_), int(last[-2].as_i()) + 1);
+    value_array* row = (value_array*) ti.allocate(shallow_size(ncol), memtag_value);
+    row->ts_ = ts;
+    row->ncol_ = ncol;
+    memcpy(row->cols_, cols_, ncol_ * sizeof(cols_[0]));
+    memset(row->cols_ + ncol_, 0, (ncol - ncol_) * sizeof(cols_[0]));
+    for (; first != last; first += 2)
+        row->cols_[first[0].as_u()] = make_column(first[1].as_s(), ti);
+    return row;
+}
+
+void value_array::deallocate(threadinfo& ti) {
+    for (short i = 0; i < ncol_; ++i)
+        deallocate_column(cols_[i], ti);
+    ti.deallocate(this, shallow_size(), memtag_value);
+}
+
+void value_array::deallocate_rcu(threadinfo& ti) {
+    for (short i = 0; i < ncol_; ++i)
+        deallocate_column_rcu(cols_[i], ti);
+    ti.deallocate_rcu(this, shallow_size(), memtag_value);
+}
+
+void value_array::deallocate_rcu_after_update(const Json* first, const Json* last, threadinfo& ti) {
+    for (; first != last && first[0].as_u() < unsigned(ncol_); first += 2)
+        deallocate_column_rcu(cols_[first[0].as_u()], ti);
+    ti.deallocate_rcu(this, shallow_size(), memtag_value);
+}
+
+void value_array::deallocate_after_failed_update(const Json* first, const Json* last, threadinfo& ti) {
+    for (; first != last; first += 2)
+        deallocate_column(cols_[first[0].as_u()], ti);
+    ti.deallocate(this, shallow_size(), memtag_value);
+}
diff --git a/silo/masstree/value_array.hh b/silo/masstree/value_array.hh
new file mode 100644 (file)
index 0000000..5191027
--- /dev/null
@@ -0,0 +1,156 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef VALUE_ARRAY_HH
+#define VALUE_ARRAY_HH
+#include "compiler.hh"
+#include "json.hh"
+
+class value_array {
+  public:
+    typedef short index_type;
+    static const char *name() { return "Array"; }
+
+    typedef lcdf::Json Json;
+
+    inline value_array();
+
+    inline kvtimestamp_t timestamp() const;
+    inline int ncol() const;
+    inline Str col(int i) const;
+
+    void deallocate(threadinfo &ti);
+    void deallocate_rcu(threadinfo &ti);
+
+    value_array* update(const Json* first, const Json* last, kvtimestamp_t ts, threadinfo& ti) const;
+    static value_array* create(const Json* first, const Json* last, kvtimestamp_t ts, threadinfo& ti);
+    static inline value_array* create1(Str value, kvtimestamp_t ts, threadinfo& ti);
+    void deallocate_rcu_after_update(const Json* first, const Json* last, threadinfo& ti);
+    void deallocate_after_failed_update(const Json* first, const Json* last, threadinfo& ti);
+
+    template <typename PARSER>
+    static value_array* checkpoint_read(PARSER& par, kvtimestamp_t ts,
+                                        threadinfo& ti);
+    template <typename UNPARSER>
+    void checkpoint_write(UNPARSER& unpar) const;
+
+    void print(FILE* f, const char* prefix, int indent, Str key,
+              kvtimestamp_t initial_ts, const char* suffix = "") {
+       kvtimestamp_t adj_ts = timestamp_sub(ts_, initial_ts);
+       fprintf(f, "%s%*s%.*s = ### @" PRIKVTSPARTS "%s\n", prefix, indent, "",
+               key.len, key.s, KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
+    }
+
+    static inline lcdf::inline_string* make_column(Str str, threadinfo& ti);
+    static void deallocate_column(lcdf::inline_string* col, threadinfo& ti);
+    static void deallocate_column_rcu(lcdf::inline_string* col, threadinfo& ti);
+
+  private:
+    kvtimestamp_t ts_;
+    short ncol_;
+    lcdf::inline_string* cols_[0];
+
+    static inline size_t shallow_size(int ncol);
+    inline size_t shallow_size() const;
+    static value_array* make_sized_row(int ncol, kvtimestamp_t ts, threadinfo& ti);
+};
+
+inline value_array::value_array()
+    : ts_(0), ncol_(0) {
+}
+
+inline kvtimestamp_t value_array::timestamp() const {
+    return ts_;
+}
+
+inline int value_array::ncol() const {
+    return ncol_;
+}
+
+inline Str value_array::col(int i) const {
+    if (unsigned(i) < unsigned(ncol_) && cols_[i])
+        return Str(cols_[i]->s, cols_[i]->len);
+    else
+        return Str();
+}
+
+inline size_t value_array::shallow_size(int ncol) {
+    return sizeof(value_array) + sizeof(lcdf::inline_string*) * ncol;
+}
+
+inline size_t value_array::shallow_size() const {
+    return shallow_size(ncol_);
+}
+
+inline lcdf::inline_string* value_array::make_column(Str str, threadinfo& ti) {
+    using lcdf::inline_string;
+    if (str) {
+        inline_string* col = (inline_string*) ti.allocate(inline_string::size(str.length()), memtag_value);
+        col->len = str.length();
+        memcpy(col->s, str.data(), str.length());
+        return col;
+    } else
+        return 0;
+}
+
+inline void value_array::deallocate_column(lcdf::inline_string* col,
+                                           threadinfo& ti) {
+    if (col)
+        ti.deallocate(col, col->size(), memtag_value);
+}
+
+inline void value_array::deallocate_column_rcu(lcdf::inline_string* col,
+                                               threadinfo& ti) {
+    if (col)
+        ti.deallocate_rcu(col, col->size(), memtag_value);
+}
+
+inline value_array* value_array::create(const Json* first, const Json* last,
+                                        kvtimestamp_t ts, threadinfo& ti) {
+    value_array empty;
+    return empty.update(first, last, ts, ti);
+}
+
+inline value_array* value_array::create1(Str value, kvtimestamp_t ts, threadinfo& ti) {
+    value_array* row = (value_array*) ti.allocate(shallow_size(1), memtag_value);
+    row->ts_ = ts;
+    row->ncol_ = 1;
+    row->cols_[0] = make_column(value, ti);
+    return row;
+}
+
+template <typename PARSER>
+value_array* value_array::checkpoint_read(PARSER& par, kvtimestamp_t ts,
+                                          threadinfo& ti) {
+    unsigned ncol;
+    par.read_array_header(ncol);
+    value_array* row = make_sized_row(ncol, ts, ti);
+    Str col;
+    for (unsigned i = 0; i != ncol; i++) {
+        par >> col;
+        if (col)
+            row->cols_[i] = make_column(col, ti);
+    }
+    return row;
+}
+
+template <typename UNPARSER>
+void value_array::checkpoint_write(UNPARSER& unpar) const {
+    unpar.write_array_header(ncol_);
+    for (short i = 0; i != ncol_; i++)
+        unpar << col(i);
+}
+
+#endif
diff --git a/silo/masstree/value_bag.hh b/silo/masstree/value_bag.hh
new file mode 100644 (file)
index 0000000..2dec89d
--- /dev/null
@@ -0,0 +1,285 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef VALUE_BAG_HH
+#define VALUE_BAG_HH
+#include "kvthread.hh"
+#include "json.hh"
+
+template <typename O>
+class value_bag {
+  public:
+    typedef short index_type;
+    typedef O offset_type;
+    typedef lcdf::Str Str;
+    typedef lcdf::Json Json;
+
+  private:
+    union bagdata {
+        struct {
+            offset_type ncol_;
+            offset_type pos_[1];
+        };
+        char s_[0];
+    };
+
+  public:
+    static const char *name() { return "Bag"; }
+
+    inline value_bag();
+
+    inline kvtimestamp_t timestamp() const;
+    inline size_t size() const;
+    inline int ncol() const;
+    inline O column_length(int i) const;
+    inline Str col(int i) const;
+
+    inline Str row_string() const;
+
+    template <typename ALLOC> inline void deallocate(ALLOC& ti);
+    template <typename ALLOC> inline void deallocate_rcu(ALLOC& ti);
+
+    template <typename ALLOC>
+    value_bag<O>* update(const Json* first, const Json* last, kvtimestamp_t ts,
+                         ALLOC& ti) const;
+    template <typename ALLOC>
+    inline value_bag<O>* update(int col, Str value,
+                                kvtimestamp_t ts, ALLOC& ti) const;
+    template <typename ALLOC>
+    static value_bag<O>* create(const Json* first, const Json* last,
+                                kvtimestamp_t ts, ALLOC& ti);
+    template <typename ALLOC>
+    static value_bag<O>* create1(Str value, kvtimestamp_t ts, ALLOC& ti);
+    template <typename ALLOC>
+    inline void deallocate_rcu_after_update(const Json* first, const Json* last, ALLOC& ti);
+    template <typename ALLOC>
+    inline void deallocate_after_failed_update(const Json* first, const Json* last, ALLOC& ti);
+
+    template <typename PARSER, typename ALLOC>
+    static value_bag<O>* checkpoint_read(PARSER& par, kvtimestamp_t ts,
+                                         ALLOC& ti);
+    template <typename UNPARSER>
+    inline void checkpoint_write(UNPARSER& unpar) const;
+
+    void print(FILE* f, const char* prefix, int indent, Str key,
+               kvtimestamp_t initial_ts, const char* suffix = "");
+
+  private:
+    kvtimestamp_t ts_;
+    bagdata d_;
+};
+
+
+template <typename O>
+inline value_bag<O>::value_bag()
+    : ts_(0) {
+    d_.ncol_ = 0;
+    d_.pos_[0] = sizeof(bagdata);
+}
+
+template <typename O>
+inline kvtimestamp_t value_bag<O>::timestamp() const {
+    return ts_;
+}
+
+template <typename O>
+inline size_t value_bag<O>::size() const {
+    return sizeof(kvtimestamp_t) + d_.pos_[d_.ncol_];
+}
+
+template <typename O>
+inline int value_bag<O>::ncol() const {
+    return d_.ncol_;
+}
+
+template <typename O>
+inline O value_bag<O>::column_length(int i) const {
+    return d_.pos_[i + 1] - d_.pos_[i];
+}
+
+template <typename O>
+inline lcdf::Str value_bag<O>::col(int i) const {
+    if (unsigned(i) < unsigned(d_.ncol_))
+        return Str(d_.s_ + d_.pos_[i], column_length(i));
+    else
+        return Str();
+}
+
+template <typename O>
+inline lcdf::Str value_bag<O>::row_string() const {
+    return Str(d_.s_, d_.pos_[d_.ncol_]);
+}
+
+template <typename O> template <typename ALLOC>
+inline void value_bag<O>::deallocate(ALLOC& ti) {
+    ti.deallocate(this, size(), memtag_value);
+}
+
+template <typename O> template <typename ALLOC>
+inline void value_bag<O>::deallocate_rcu(ALLOC& ti) {
+    ti.deallocate_rcu(this, size(), memtag_value);
+}
+
+// prerequisite: [first, last) is an array [column, value, column, value, ...]
+// each column is unsigned; the columns are strictly increasing;
+// each value is a string
+template <typename O> template <typename ALLOC>
+value_bag<O>* value_bag<O>::update(const Json* first, const Json* last,
+                                   kvtimestamp_t ts, ALLOC& ti) const
+{
+    size_t sz = size();
+    unsigned ncol = d_.ncol_;
+    for (auto it = first; it != last; it += 2) {
+        unsigned idx = it[0].as_u();
+        sz += it[1].as_s().length();
+        if (idx < d_.ncol_)
+            sz -= column_length(idx);
+        else
+            ncol = idx + 1;
+    }
+    if (ncol > d_.ncol_)
+        sz += (ncol - d_.ncol_) * sizeof(offset_type);
+
+    value_bag<O>* row = (value_bag<O>*) ti.allocate(sz, memtag_value);
+    row->ts_ = ts;
+
+    // Minor optimization: Replacing one small column without changing length
+    if (ncol == d_.ncol_ && sz == size() && first + 2 == last
+        && first[1].as_s().length() <= 16) {
+        memcpy(row->d_.s_, d_.s_, sz - sizeof(kvtimestamp_t));
+        memcpy(row->d_.s_ + d_.pos_[first[0].as_u()],
+               first[1].as_s().data(), first[1].as_s().length());
+        return row;
+    }
+
+    // Otherwise need to do more work
+    row->d_.ncol_ = ncol;
+    sz = sizeof(bagdata) + ncol * sizeof(offset_type);
+    unsigned col = 0;
+    while (1) {
+        unsigned this_col = (first != last ? first[0].as_u() : ncol);
+
+        // copy data from old row
+        if (col != this_col && col < d_.ncol_) {
+            unsigned end_col = std::min(this_col, unsigned(d_.ncol_));
+            ssize_t delta = sz - d_.pos_[col];
+            if (delta == 0)
+                memcpy(row->d_.pos_ + col, d_.pos_ + col,
+                       sizeof(offset_type) * (end_col - col));
+            else
+                for (unsigned i = col; i < end_col; ++i)
+                    row->d_.pos_[i] = d_.pos_[i] + delta;
+            size_t amt = d_.pos_[end_col] - d_.pos_[col];
+            memcpy(row->d_.s_ + sz, d_.s_ + d_.pos_[col], amt);
+            sz += amt;
+            col = end_col;
+        }
+
+        // mark empty columns if we're extending
+        while (col != this_col) {
+            row->d_.pos_[col] = sz;
+            ++col;
+        }
+
+        if (col == ncol)
+            break;
+
+        // copy data from change
+        row->d_.pos_[col] = sz;
+        Str val = first[1].as_s();
+        memcpy(row->d_.s_ + sz, val.data(), val.length());
+        sz += val.length();
+        first += 2;
+        ++col;
+    }
+    row->d_.pos_[ncol] = sz;
+    return row;
+}
+
+template <typename O> template <typename ALLOC>
+inline value_bag<O>* value_bag<O>::update(int col, Str value, kvtimestamp_t ts,
+                                          ALLOC& ti) const {
+    Json change[2] = {Json(col), Json(value)};
+    return update(&change[0], &change[2], ts, ti);
+}
+
+template <typename O> template <typename ALLOC>
+inline value_bag<O>* value_bag<O>::create(const Json* first, const Json* last,
+                                          kvtimestamp_t ts, ALLOC& ti) {
+    value_bag<O> empty;
+    return empty.update(first, last, ts, ti);
+}
+
+template <typename O> template <typename ALLOC>
+inline value_bag<O>* value_bag<O>::create1(Str str, kvtimestamp_t ts,
+                                           ALLOC& ti) {
+    value_bag<O>* row = (value_bag<O>*) ti.allocate(sizeof(kvtimestamp_t) + sizeof(bagdata) + sizeof(O) + str.length(), memtag_value);
+    row->ts_ = ts;
+    row->d_.ncol_ = 1;
+    row->d_.pos_[0] = sizeof(bagdata) + sizeof(O);
+    row->d_.pos_[1] = sizeof(bagdata) + sizeof(O) + str.length();
+    memcpy(row->d_.s_ + row->d_.pos_[0], str.data(), str.length());
+    return row;
+}
+
+template <typename O> template <typename ALLOC>
+inline void value_bag<O>::deallocate_rcu_after_update(const Json*, const Json*, ALLOC& ti) {
+    deallocate_rcu(ti);
+}
+
+template <typename O> template <typename ALLOC>
+inline void value_bag<O>::deallocate_after_failed_update(const Json*, const Json*, ALLOC& ti) {
+    deallocate(ti);
+}
+
+template <typename O> template <typename PARSER, typename ALLOC>
+inline value_bag<O>* value_bag<O>::checkpoint_read(PARSER& par,
+                                                   kvtimestamp_t ts,
+                                                   ALLOC& ti) {
+    Str value;
+    par >> value;
+    value_bag<O>* row = (value_bag<O>*) ti.allocate(sizeof(kvtimestamp_t) + value.length(), memtag_value);
+    row->ts_ = ts;
+    memcpy(row->d_.s_, value.data(), value.length());
+    return row;
+}
+
+template <typename O> template <typename UNPARSER>
+inline void value_bag<O>::checkpoint_write(UNPARSER& unpar) const {
+    unpar << Str(d_.s_, d_.pos_[d_.ncol_]);
+}
+
+template <typename O>
+void value_bag<O>::print(FILE *f, const char *prefix, int indent,
+                         Str key, kvtimestamp_t initial_ts,
+                         const char *suffix)
+{
+    kvtimestamp_t adj_ts = timestamp_sub(ts_, initial_ts);
+    if (d_.ncol_ == 1)
+        fprintf(f, "%s%*s%.*s = %.*s @" PRIKVTSPARTS "%s\n", prefix, indent, "",
+                key.len, key.s, d_.pos_[1] - d_.pos_[0], d_.s_ + d_.pos_[0],
+                KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
+    else {
+        fprintf(f, "%s%*s%.*s = [", prefix, indent, "", key.len, key.s);
+        for (int col = 0; col < d_.ncol_; ++col) {
+            int pos = d_.pos_[col], len = std::min(40, d_.pos_[col + 1] - pos);
+            fprintf(f, col ? "|%.*s" : "%.*s", len, d_.s_ + pos);
+        }
+        fprintf(f, "] @" PRIKVTSPARTS "%s\n",
+                KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
+    }
+}
+
+#endif
diff --git a/silo/masstree/value_string.cc b/silo/masstree/value_string.cc
new file mode 100644 (file)
index 0000000..9ab70e3
--- /dev/null
@@ -0,0 +1,18 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "kvrow.hh"
+#include "value_string.hh"
+#include <string.h>
diff --git a/silo/masstree/value_string.hh b/silo/masstree/value_string.hh
new file mode 100644 (file)
index 0000000..dc35c40
--- /dev/null
@@ -0,0 +1,193 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef VALUE_STRING_HH
+#define VALUE_STRING_HH
+#include "compiler.hh"
+#include "json.hh"
+
+class value_string {
+  public:
+    typedef unsigned index_type;
+    static const char *name() { return "String"; }
+
+    typedef lcdf::Str Str;
+    typedef lcdf::Json Json;
+
+    inline value_string();
+
+    inline kvtimestamp_t timestamp() const;
+    inline size_t size() const;
+    inline int ncol() const;
+    inline Str col(index_type idx) const;
+
+    template <typename ALLOC>
+    inline void deallocate(ALLOC& ti);
+    inline void deallocate_rcu(threadinfo& ti);
+
+    template <typename ALLOC>
+    value_string* update(const Json* first, const Json* last, kvtimestamp_t ts, ALLOC& ti) const;
+    static inline value_string* create(const Json* first, const Json* last, kvtimestamp_t ts, threadinfo& ti);
+    static inline value_string* create1(Str value, kvtimestamp_t ts, threadinfo& ti);
+    inline void deallocate_rcu_after_update(const Json* first, const Json* last, threadinfo& ti);
+    inline void deallocate_after_failed_update(const Json* first, const Json* last, threadinfo& ti);
+
+    template <typename PARSER>
+    static inline value_string* checkpoint_read(PARSER& par, kvtimestamp_t ts,
+                                                threadinfo& ti);
+    template <typename UNPARSER>
+    inline void checkpoint_write(UNPARSER& unpar) const;
+
+    void print(FILE* f, const char* prefix, int indent, Str key,
+               kvtimestamp_t initial_ts, const char* suffix = "") {
+        kvtimestamp_t adj_ts = timestamp_sub(ts_, initial_ts);
+        fprintf(f, "%s%*s%.*s = %.*s @" PRIKVTSPARTS "%s\n", prefix, indent, "",
+                key.len, key.s, std::min(40U, vallen_), s_,
+                KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
+    }
+
+    static inline index_type make_index(unsigned offset, unsigned length);
+    static inline unsigned index_offset(index_type idx);
+    static inline unsigned index_length(index_type idx);
+
+  private:
+    kvtimestamp_t ts_;
+    unsigned vallen_;
+    char s_[0];
+
+    static inline unsigned index_last_offset(index_type idx);
+    static inline size_t shallow_size(int vallen);
+    inline size_t shallow_size() const;
+};
+
+inline value_string::index_type value_string::make_index(unsigned offset, unsigned length) {
+    return offset + (length << 16);
+}
+
+inline unsigned value_string::index_offset(index_type idx) {
+    return idx & 0xFFFF;
+}
+
+inline unsigned value_string::index_length(index_type idx) {
+    return idx >> 16;
+}
+
+inline value_string::value_string()
+    : ts_(0), vallen_(0) {
+}
+
+inline kvtimestamp_t value_string::timestamp() const {
+    return ts_;
+}
+
+inline size_t value_string::size() const {
+    return sizeof(value_string) + vallen_;
+}
+
+inline int value_string::ncol() const {
+    return 1;
+}
+
+inline unsigned value_string::index_last_offset(index_type idx) {
+    return index_offset(idx) + index_length(idx);
+}
+
+inline lcdf::Str value_string::col(index_type idx) const {
+    if (idx == 0)
+        return Str(s_, vallen_);
+    else {
+        unsigned off = std::min(vallen_, index_offset(idx));
+        return Str(s_ + off, std::min(vallen_ - off, index_length(idx)));
+    }
+}
+
+template <typename ALLOC>
+inline void value_string::deallocate(ALLOC& ti) {
+    ti.deallocate(this, size(), memtag_value);
+}
+
+inline void value_string::deallocate_rcu(threadinfo& ti) {
+    ti.deallocate_rcu(this, size(), memtag_value);
+}
+
+inline size_t value_string::shallow_size(int vallen) {
+    return sizeof(value_string) + vallen;
+}
+
+inline size_t value_string::shallow_size() const {
+    return shallow_size(vallen_);
+}
+
+template <typename ALLOC>
+value_string* value_string::update(const Json* first, const Json* last,
+                                   kvtimestamp_t ts, ALLOC& ti) const {
+    unsigned vallen = 0, cut = vallen_;
+    for (auto it = first; it != last; it += 2) {
+        unsigned idx = it[0].as_u(), length = it[1].as_s().length();
+        if (idx == 0)
+            cut = length;
+        vallen = std::max(vallen, index_offset(idx) + length);
+    }
+    vallen = std::max(vallen, cut);
+    value_string* row = (value_string*) ti.allocate(shallow_size(vallen), memtag_value);
+    row->ts_ = ts;
+    row->vallen_ = vallen;
+    memcpy(row->s_, s_, cut);
+    for (; first != last; first += 2) {
+        Str val = first[1].as_s();
+        memcpy(row->s_ + index_offset(first[0].as_u()), val.data(), val.length());
+    }
+    return row;
+}
+
+inline value_string* value_string::create(const Json* first, const Json* last,
+                                          kvtimestamp_t ts, threadinfo& ti) {
+    value_string empty;
+    return empty.update(first, last, ts, ti);
+}
+
+inline value_string* value_string::create1(Str value,
+                                           kvtimestamp_t ts,
+                                           threadinfo& ti) {
+    value_string* row = (value_string*) ti.allocate(shallow_size(value.length()), memtag_value);
+    row->ts_ = ts;
+    row->vallen_ = value.length();
+    memcpy(row->s_, value.data(), value.length());
+    return row;
+}
+
+inline void value_string::deallocate_rcu_after_update(const Json*, const Json*, threadinfo& ti) {
+    deallocate_rcu(ti);
+}
+
+inline void value_string::deallocate_after_failed_update(const Json*, const Json*, threadinfo& ti) {
+    deallocate(ti);
+}
+
+template <typename PARSER>
+inline value_string* value_string::checkpoint_read(PARSER& par,
+                                                   kvtimestamp_t ts,
+                                                   threadinfo& ti) {
+    Str str;
+    par >> str;
+    return create1(str, ts, ti);
+}
+
+template <typename UNPARSER>
+inline void value_string::checkpoint_write(UNPARSER& unpar) const {
+    unpar << Str(s_, vallen_);
+}
+
+#endif
diff --git a/silo/masstree/value_versioned_array.cc b/silo/masstree/value_versioned_array.cc
new file mode 100644 (file)
index 0000000..dadd352
--- /dev/null
@@ -0,0 +1,97 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#include "kvrow.hh"
+#include "value_versioned_array.hh"
+#include <string.h>
+
+value_versioned_array* value_versioned_array::make_sized_row(int ncol, kvtimestamp_t ts, threadinfo& ti) {
+    value_versioned_array* row = (value_versioned_array*) ti.allocate(shallow_size(ncol), memtag_value);
+    row->ts_ = ts;
+    row->ver_ = rowversion();
+    row->ncol_ = row->ncol_cap_ = ncol;
+    memset(row->cols_, 0, sizeof(row->cols_[0]) * ncol);
+    return row;
+}
+
+void value_versioned_array::snapshot(value_versioned_array*& storage,
+                                     const std::vector<index_type>& f, threadinfo& ti) const {
+    if (!storage || storage->ncol_cap_ < ncol_) {
+        if (storage)
+            storage->deallocate(ti);
+        storage = make_sized_row(ncol_, ts_, ti);
+    }
+    storage->ncol_ = ncol_;
+    rowversion v1 = ver_.stable();
+    while (1) {
+        if (f.size() == 1)
+            storage->cols_[f[0]] = cols_[f[0]];
+        else
+            memcpy(storage->cols_, cols_, sizeof(cols_[0]) * storage->ncol_);
+        rowversion v2 = ver_.stable();
+        if (!v1.has_changed(v2))
+            break;
+        v1 = v2;
+    }
+}
+
+value_versioned_array*
+value_versioned_array::update(const Json* first, const Json* last,
+                              kvtimestamp_t ts, threadinfo& ti,
+                              bool always_copy) {
+    int ncol = last[-2].as_u() + 1;
+    value_versioned_array* row;
+    if (ncol > ncol_cap_ || always_copy) {
+        row = (value_versioned_array*) ti.allocate(shallow_size(ncol), memtag_value);
+        row->ts_ = ts;
+        row->ver_ = rowversion();
+        row->ncol_ = row->ncol_cap_ = ncol;
+        memcpy(row->cols_, cols_, sizeof(cols_[0]) * ncol_);
+    } else
+        row = this;
+    if (ncol > ncol_)
+        memset(row->cols_ + ncol_, 0, sizeof(cols_[0]) * (ncol - ncol_));
+
+    if (row == this) {
+        ver_.setdirty();
+        fence();
+    }
+    if (row->ncol_ < ncol)
+        row->ncol_ = ncol;
+
+    for (; first != last; first += 2) {
+        unsigned idx = first[0].as_u();
+        value_array::deallocate_column_rcu(row->cols_[idx], ti);
+        row->cols_[idx] = value_array::make_column(first[1].as_s(), ti);
+    }
+
+    if (row == this) {
+        fence();
+        ver_.clearandbump();
+    }
+    return row;
+}
+
+void value_versioned_array::deallocate(threadinfo &ti) {
+    for (short i = 0; i < ncol_; ++i)
+        value_array::deallocate_column(cols_[i], ti);
+    ti.deallocate(this, shallow_size(), memtag_value);
+}
+
+void value_versioned_array::deallocate_rcu(threadinfo &ti) {
+    for (short i = 0; i < ncol_; ++i)
+        value_array::deallocate_column_rcu(cols_[i], ti);
+    ti.deallocate_rcu(this, shallow_size(), memtag_value);
+}
diff --git a/silo/masstree/value_versioned_array.hh b/silo/masstree/value_versioned_array.hh
new file mode 100644 (file)
index 0000000..9f7a3b9
--- /dev/null
@@ -0,0 +1,205 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2014 President and Fellows of Harvard College
+ * Copyright (c) 2012-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef VALUE_VERSIONED_ARRAY_HH
+#define VALUE_VERSIONED_ARRAY_HH
+#include "compiler.hh"
+#include "value_array.hh"
+
+struct rowversion {
+    rowversion() {
+        v_.u = 0;
+    }
+    bool dirty() {
+        return v_.dirty;
+    }
+    void setdirty() {
+        v_.u = v_.u | 0x80000000;
+    }
+    void clear() {
+        v_.u = v_.u & 0x7fffffff;
+    }
+    void clearandbump() {
+        v_.u = (v_.u + 1) & 0x7fffffff;
+    }
+    rowversion stable() const {
+        value_t x = v_;
+        while (x.dirty) {
+            relax_fence();
+            x = v_;
+        }
+        acquire_fence();
+        return x;
+    }
+    bool has_changed(rowversion x) const {
+        fence();
+        return x.v_.ctr != v_.ctr;
+    }
+  private:
+    union value_t {
+        struct {
+            uint32_t ctr:31;
+            uint32_t dirty:1;
+        };
+        uint32_t u;
+    };
+    value_t v_;
+
+    rowversion(value_t v)
+        : v_(v) {
+    }
+
+};
+
+class value_versioned_array {
+  public:
+    typedef value_array::index_type index_type;
+    static const char *name() { return "ArrayVersion"; }
+
+    typedef lcdf::Json Json;
+
+    inline value_versioned_array();
+
+    inline kvtimestamp_t timestamp() const;
+    inline int ncol() const;
+    inline Str col(int i) const;
+
+    void deallocate(threadinfo &ti);
+    void deallocate_rcu(threadinfo &ti);
+
+    void snapshot(value_versioned_array*& storage,
+                  const std::vector<index_type>& f, threadinfo& ti) const;
+
+    value_versioned_array* update(const Json* first, const Json* last,
+                                  kvtimestamp_t ts, threadinfo& ti,
+                                  bool always_copy = false);
+    static value_versioned_array* create(const Json* first, const Json* last,
+                                         kvtimestamp_t ts, threadinfo& ti);
+    static value_versioned_array* create1(Str value, kvtimestamp_t ts, threadinfo& ti);
+    inline void deallocate_rcu_after_update(const Json* first, const Json* last, threadinfo& ti);
+    inline void deallocate_after_failed_update(const Json* first, const Json* last, threadinfo& ti);
+
+    template <typename PARSER>
+    static value_versioned_array* checkpoint_read(PARSER& par, kvtimestamp_t ts,
+                                                  threadinfo& ti);
+    template <typename UNPARSER>
+    void checkpoint_write(UNPARSER& unpar) const;
+
+    void print(FILE *f, const char *prefix, int indent, Str key,
+               kvtimestamp_t initial_ts, const char *suffix = "") {
+        kvtimestamp_t adj_ts = timestamp_sub(ts_, initial_ts);
+        fprintf(f, "%s%*s%.*s = ### @" PRIKVTSPARTS "%s\n", prefix, indent, "",
+                key.len, key.s, KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
+    }
+
+  private:
+    kvtimestamp_t ts_;
+    rowversion ver_;
+    short ncol_;
+    short ncol_cap_;
+    lcdf::inline_string* cols_[0];
+
+    static inline size_t shallow_size(int ncol);
+    inline size_t shallow_size() const;
+    static value_versioned_array* make_sized_row(int ncol, kvtimestamp_t ts, threadinfo& ti);
+};
+
+template <>
+struct query_helper<value_versioned_array> {
+    value_versioned_array* snapshot_;
+
+    query_helper()
+        : snapshot_() {
+    }
+    inline const value_versioned_array* snapshot(const value_versioned_array* row,
+                                                 const std::vector<value_versioned_array::index_type>& f,
+                                                 threadinfo& ti) {
+        row->snapshot(snapshot_, f, ti);
+        return snapshot_;
+    }
+};
+
+inline value_versioned_array::value_versioned_array()
+    : ts_(0), ncol_(0), ncol_cap_(0) {
+}
+
+inline kvtimestamp_t value_versioned_array::timestamp() const {
+    return ts_;
+}
+
+inline int value_versioned_array::ncol() const {
+    return ncol_;
+}
+
+inline Str value_versioned_array::col(int i) const {
+    if (unsigned(i) < unsigned(ncol_) && cols_[i])
+        return Str(cols_[i]->s, cols_[i]->len);
+    else
+        return Str();
+}
+
+inline size_t value_versioned_array::shallow_size(int ncol) {
+    return sizeof(value_versioned_array) + ncol * sizeof(lcdf::inline_string*);
+}
+
+inline size_t value_versioned_array::shallow_size() const {
+    return shallow_size(ncol_);
+}
+
+inline value_versioned_array* value_versioned_array::create(const Json* first, const Json* last, kvtimestamp_t ts, threadinfo& ti) {
+    value_versioned_array empty;
+    return empty.update(first, last, ts, ti, true);
+}
+
+inline value_versioned_array* value_versioned_array::create1(Str value, kvtimestamp_t ts, threadinfo& ti) {
+    value_versioned_array* row = (value_versioned_array*) ti.allocate(shallow_size(1), memtag_value);
+    row->ts_ = ts;
+    row->ver_ = rowversion();
+    row->ncol_ = row->ncol_cap_ = 1;
+    row->cols_[0] = value_array::make_column(value, ti);
+    return row;
+}
+
+inline void value_versioned_array::deallocate_rcu_after_update(const Json*, const Json*, threadinfo& ti) {
+    ti.deallocate_rcu(this, shallow_size(), memtag_value);
+}
+
+inline void value_versioned_array::deallocate_after_failed_update(const Json*, const Json*, threadinfo&) {
+    always_assert(0);
+}
+
+template <typename PARSER>
+value_versioned_array*
+value_versioned_array::checkpoint_read(PARSER& par, kvtimestamp_t ts,
+                                       threadinfo& ti) {
+    unsigned ncol;
+    par.read_array_header(ncol);
+    value_versioned_array* row = make_sized_row(ncol, ts, ti);
+    Str col;
+    for (unsigned i = 0; i != ncol; i++) {
+        par >> col;
+        row->cols_[i] = value_array::make_column(col, ti);
+    }
+    return row;
+}
+
+template <typename UNPARSER>
+void value_versioned_array::checkpoint_write(UNPARSER& unpar) const {
+    unpar.write_array_header(ncol_);
+    for (short i = 0; i != ncol_; ++i)
+        unpar << col(i);
+}
+
+#endif
diff --git a/silo/masstree_btree.h b/silo/masstree_btree.h
new file mode 100644 (file)
index 0000000..bbb259d
--- /dev/null
@@ -0,0 +1,812 @@
+// -*- c-basic-offset: 2 -*-
+#pragma once
+
+#include <assert.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <utility>
+#include <atomic>
+
+#include "log2.hh"
+#include "ndb_type_traits.h"
+#include "varkey.h"
+#include "counter.h"
+#include "macros.h"
+#include "prefetch.h"
+#include "amd64.h"
+#include "rcu.h"
+#include "util.h"
+#include "small_vector.h"
+#include "ownership_checker.h"
+
+#include "masstree/masstree_scan.hh"
+#include "masstree/masstree_insert.hh"
+#include "masstree/masstree_remove.hh"
+#include "masstree/masstree_print.hh"
+#include "masstree/timestamp.hh"
+#include "masstree/mtcounters.hh"
+#include "masstree/circular_int.hh"
+
+class simple_threadinfo {
+ public:
+    simple_threadinfo()
+        : ts_(0) { // XXX?
+    }
+    class rcu_callback {
+    public:
+      virtual void operator()(simple_threadinfo& ti) = 0;
+    };
+
+ private:
+    static inline void rcu_callback_function(void* p) {
+      simple_threadinfo ti;
+      static_cast<rcu_callback*>(p)->operator()(ti);
+    }
+
+ public:
+    // XXX Correct node timstamps are needed for recovery, but for no other
+    // reason.
+    kvtimestamp_t operation_timestamp() const {
+      return 0;
+    }
+    kvtimestamp_t update_timestamp() const {
+       return ts_;
+    }
+    kvtimestamp_t update_timestamp(kvtimestamp_t x) const {
+       if (circular_int<kvtimestamp_t>::less_equal(ts_, x))
+           // x might be a marker timestamp; ensure result is not
+           ts_ = (x | 1) + 1;
+       return ts_;
+    }
+    kvtimestamp_t update_timestamp(kvtimestamp_t x, kvtimestamp_t y) const {
+       if (circular_int<kvtimestamp_t>::less(x, y))
+           x = y;
+       if (circular_int<kvtimestamp_t>::less_equal(ts_, x))
+           // x might be a marker timestamp; ensure result is not
+           ts_ = (x | 1) + 1;
+       return ts_;
+    }
+    void increment_timestamp() {
+       ts_ += 2;
+    }
+    void advance_timestamp(kvtimestamp_t x) {
+       if (circular_int<kvtimestamp_t>::less(ts_, x))
+           ts_ = x;
+    }
+
+    // event counters
+    void mark(threadcounter) {
+    }
+    void mark(threadcounter, int64_t) {
+    }
+    bool has_counter(threadcounter) const {
+        return false;
+    }
+    uint64_t counter(threadcounter ci) const {
+       return 0;
+    }
+
+    /** @brief Return a function object that calls mark(ci); relax_fence().
+     *
+     * This function object can be used to count the number of relax_fence()s
+     * executed. */
+    relax_fence_function accounting_relax_fence(threadcounter) {
+       return relax_fence_function();
+    }
+
+    class accounting_relax_fence_function {
+    public:
+      template <typename V>
+      void operator()(V) {
+        relax_fence();
+      }
+    };
+    /** @brief Return a function object that calls mark(ci); relax_fence().
+     *
+     * This function object can be used to count the number of relax_fence()s
+     * executed. */
+    accounting_relax_fence_function stable_fence() {
+       return accounting_relax_fence_function();
+    }
+
+    relax_fence_function lock_fence(threadcounter) {
+       return relax_fence_function();
+    }
+
+    // memory allocation
+    void* allocate(size_t sz, memtag) {
+        return rcu::s_instance.alloc(sz);
+    }
+    void deallocate(void* p, size_t sz, memtag) {
+       // in C++ allocators, 'p' must be nonnull
+        rcu::s_instance.dealloc(p, sz);
+    }
+    void deallocate_rcu(void *p, size_t sz, memtag) {
+       assert(p);
+        rcu::s_instance.dealloc_rcu(p, sz);
+    }
+
+    void* pool_allocate(size_t sz, memtag) {
+       int nl = (sz + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE;
+        return rcu::s_instance.alloc(nl * CACHE_LINE_SIZE);
+    }
+    void pool_deallocate(void* p, size_t sz, memtag) {
+       int nl = (sz + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE;
+        rcu::s_instance.dealloc(p, nl * CACHE_LINE_SIZE);
+    }
+    void pool_deallocate_rcu(void* p, size_t sz, memtag) {
+       assert(p);
+       int nl = (sz + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE;
+        rcu::s_instance.dealloc_rcu(p, nl * CACHE_LINE_SIZE);
+    }
+
+    // RCU
+    void rcu_register(rcu_callback *cb) {
+      scoped_rcu_base<false> guard;
+      rcu::s_instance.free_with_fn(cb, rcu_callback_function);
+    }
+
+  private:
+    mutable kvtimestamp_t ts_;
+};
+
+struct masstree_params : public Masstree::nodeparams<> {
+  typedef uint8_t* value_type;
+  typedef Masstree::value_print<value_type> value_print_type;
+  typedef simple_threadinfo threadinfo_type;
+  enum { RcuRespCaller = true };
+};
+
+struct masstree_single_threaded_params : public masstree_params {
+  static constexpr bool concurrent = false;
+};
+
+template <typename P>
+class mbtree {
+ public:
+  typedef Masstree::node_base<P> node_base_type;
+  typedef Masstree::internode<P> internode_type;
+  typedef Masstree::leaf<P> leaf_type;
+  typedef Masstree::leaf<P> node_type;
+  typedef typename node_base_type::nodeversion_type nodeversion_type;
+
+  typedef varkey key_type;
+  typedef lcdf::Str string_type;
+  typedef uint64_t key_slice;
+  typedef typename P::value_type value_type;
+  typedef typename P::threadinfo_type threadinfo;
+  typedef typename std::conditional<!P::RcuRespCaller,
+      scoped_rcu_region,
+      disabled_rcu_region>::type rcu_region;
+
+  // public to assist in testing
+  static const unsigned int NKeysPerNode    = P::leaf_width;
+  static const unsigned int NMinKeysPerNode = P::leaf_width / 2;
+
+  // XXX(stephentu): trying out a very opaque node API for now
+  typedef node_type node_opaque_t;
+  typedef std::pair< const node_opaque_t *, uint64_t > versioned_node_t;
+  struct insert_info_t {
+    const node_opaque_t* node;
+    uint64_t old_version;
+    uint64_t new_version;
+  };
+
+  void invariant_checker() {} // stub for now
+
+#ifdef BTREE_LOCK_OWNERSHIP_CHECKING
+public:
+  static inline void
+  NodeLockRegionBegin()
+  {
+    // XXX: implement me
+    ALWAYS_ASSERT(false);
+    //ownership_checker<mbtree<P>, node_base_type>::NodeLockRegionBegin();
+  }
+  static inline void
+  AssertAllNodeLocksReleased()
+  {
+    // XXX: implement me
+    ALWAYS_ASSERT(false);
+    //ownership_checker<mbtree<P>, node_base_type>::AssertAllNodeLocksReleased();
+  }
+private:
+  static inline void
+  AddNodeToLockRegion(const node_base_type *n)
+  {
+    // XXX: implement me
+    ALWAYS_ASSERT(false);
+    //ownership_checker<mbtree<P>, node_base_type>::AddNodeToLockRegion(n);
+  }
+public:
+#endif
+
+  mbtree() {
+    threadinfo ti;
+    table_.initialize(ti);
+  }
+
+  ~mbtree() {
+    rcu_region guard;
+    threadinfo ti;
+    table_.destroy(ti);
+  }
+
+  /**
+   * NOT THREAD SAFE
+   */
+  inline void clear() {
+    rcu_region guard;
+    threadinfo ti;
+    table_.destroy(ti);
+    table_.initialize(ti);
+  }
+
+  /** Note: invariant checking is not thread safe */
+  inline void invariant_checker() const {
+  }
+
+          /** NOTE: the public interface assumes that the caller has taken care
+           * of setting up RCU */
+
+  inline bool search(const key_type &k, value_type &v,
+                     versioned_node_t *search_info = nullptr) const;
+
+  /**
+   * The low level callback interface is as follows:
+   *
+   * Consider a scan in the range [a, b):
+   *   1) on_resp_node() is called at least once per node which
+   *      has a responibility range that overlaps with the scan range
+   *   2) invoke() is called per <k, v>-pair such that k is in [a, b)
+   *
+   * The order of calling on_resp_node() and invoke() is up to the implementation.
+   */
+  class low_level_search_range_callback {
+  public:
+    virtual ~low_level_search_range_callback() {}
+
+    /**
+     * This node lies within the search range (at version v)
+     */
+    virtual void on_resp_node(const node_opaque_t *n, uint64_t version) = 0;
+
+    /**
+     * This key/value pair was read from node n @ version
+     */
+    virtual bool invoke(const string_type &k, value_type v,
+                        const node_opaque_t *n, uint64_t version) = 0;
+  };
+
+  /**
+   * For all keys in [lower, *upper), invoke callback in ascending order.
+   * If upper is NULL, then there is no upper bound
+   *
+
+   * This function by default provides a weakly consistent view of the b-tree. For
+   * instance, consider the following tree, where n = 3 is the max number of
+   * keys in a node:
+   *
+   *              [D|G]
+   *             /  |  \
+   *            /   |   \
+   *           /    |    \
+   *          /     |     \
+   *   [A|B|C]<->[D|E|F]<->[G|H|I]
+   *
+   * Suppose we want to scan [A, inf), so we traverse to the leftmost leaf node
+   * and start a left-to-right walk. Suppose we have emitted keys A, B, and C,
+   * and we are now just about to scan the middle leaf node.  Now suppose
+   * another thread concurrently does delete(A), followed by a delete(H).  Now
+   * the scaning thread resumes and emits keys D, E, F, G, and I, omitting H
+   * because H was deleted. This is an inconsistent view of the b-tree, since
+   * the scanning thread has observed the deletion of H but did not observe the
+   * deletion of A, but we know that delete(A) happens before delete(H).
+   *
+   * The weakly consistent guarantee provided is the following: all keys
+   * which, at the time of invocation, are known to exist in the btree
+   * will be discovered on a scan (provided the key falls within the scan's range),
+   * and provided there are no concurrent modifications/removals of that key
+   *
+   * Note that scans within a single node are consistent
+   *
+   * XXX: add other modes which provide better consistency:
+   * A) locking mode
+   * B) optimistic validation mode
+   *
+   * the last string parameter is an optional string buffer to use:
+   * if null, a stack allocated string will be used. if not null, must
+   * ensure:
+   *   A) buf->empty() at the beginning
+   *   B) no concurrent mutation of string
+   * note that string contents upon return are arbitrary
+   */
+  void
+  search_range_call(const key_type &lower,
+                    const key_type *upper,
+                    low_level_search_range_callback &callback,
+                    std::string *buf = nullptr) const;
+
+  // (lower, upper]
+  void
+  rsearch_range_call(const key_type &upper,
+                     const key_type *lower,
+                     low_level_search_range_callback &callback,
+                     std::string *buf = nullptr) const;
+
+  class search_range_callback : public low_level_search_range_callback {
+  public:
+    virtual void
+    on_resp_node(const node_opaque_t *n, uint64_t version)
+    {
+    }
+
+    virtual bool
+    invoke(const string_type &k, value_type v,
+           const node_opaque_t *n, uint64_t version)
+    {
+      return invoke(k, v);
+    }
+
+    virtual bool invoke(const string_type &k, value_type v) = 0;
+  };
+
+  /**
+   * [lower, *upper)
+   *
+   * Callback is expected to implement bool operator()(key_slice k, value_type v),
+   * where the callback returns true if it wants to keep going, false otherwise
+   */
+  template <typename F>
+  inline void
+  search_range(const key_type &lower,
+               const key_type *upper,
+               F& callback,
+               std::string *buf = nullptr) const;
+
+  /**
+   * (*lower, upper]
+   *
+   * Callback is expected to implement bool operator()(key_slice k, value_type v),
+   * where the callback returns true if it wants to keep going, false otherwise
+   */
+  template <typename F>
+  inline void
+  rsearch_range(const key_type &upper,
+                const key_type *lower,
+                F& callback,
+                std::string *buf = nullptr) const;
+
+  /**
+   * returns true if key k did not already exist, false otherwise
+   * If k exists with a different mapping, still returns false
+   *
+   * If false and old_v is not NULL, then the overwritten value of v
+   * is written into old_v
+   */
+  inline bool
+  insert(const key_type &k, value_type v,
+         value_type *old_v = NULL,
+         insert_info_t *insert_info = NULL);
+
+  /**
+   * Only puts k=>v if k does not exist in map. returns true
+   * if k inserted, false otherwise (k exists already)
+   */
+  inline bool
+  insert_if_absent(const key_type &k, value_type v,
+                   insert_info_t *insert_info = NULL);
+
+  /**
+   * return true if a value was removed, false otherwise.
+   *
+   * if true and old_v is not NULL, then the removed value of v
+   * is written into old_v
+   */
+  inline bool
+  remove(const key_type &k, value_type *old_v = NULL);
+
+  /**
+   * The tree walk API is a bit strange, due to the optimistic nature of the
+   * btree.
+   *
+   * The way it works is that, on_node_begin() is first called. In
+   * on_node_begin(), a callback function should read (but not modify) the
+   * values it is interested in, and save them.
+   *
+   * Then, either one of on_node_success() or on_node_failure() is called. If
+   * on_node_success() is called, then the previous values read in
+   * on_node_begin() are indeed valid.  If on_node_failure() is called, then
+   * the previous values are not valid and should be discarded.
+   */
+  class tree_walk_callback {
+  public:
+    virtual ~tree_walk_callback() {}
+    virtual void on_node_begin(const node_opaque_t *n) = 0;
+    virtual void on_node_success() = 0;
+    virtual void on_node_failure() = 0;
+  };
+
+  void tree_walk(tree_walk_callback &callback) const;
+
+  /**
+   * Is thread-safe, but not really designed to perform well with concurrent
+   * modifications. also the value returned is not consistent given concurrent
+   * modifications
+   */
+  inline size_t size() const;
+
+  static inline uint64_t
+  ExtractVersionNumber(const node_opaque_t *n) {
+    // XXX(stephentu): I think we must use stable_version() for
+    // correctness, but I am not 100% sure. It's definitely correct to use it,
+    // but maybe we can get away with unstable_version()?
+    return n->full_version_value();
+  }
+
+  // [value, has_suffix]
+  static std::vector< std::pair<value_type, bool> >
+  ExtractValues(const node_opaque_t *n);
+
+  /**
+   * Not well defined if n is being concurrently modified, just for debugging
+   */
+  static std::string
+  NodeStringify(const node_opaque_t *n);
+
+  void print();
+
+  static inline size_t InternalNodeSize() {
+    return sizeof(internode_type);
+  }
+
+  static inline size_t LeafNodeSize() {
+    return sizeof(leaf_type);
+  }
+
+ private:
+  Masstree::basic_table<P> table_;
+
+  static leaf_type* leftmost_descend_layer(node_base_type* n);
+  class size_walk_callback;
+  template <bool Reverse> class search_range_scanner_base;
+  template <bool Reverse> class low_level_search_range_scanner;
+  template <typename F> class low_level_search_range_callback_wrapper;
+};
+
+template <typename P>
+typename mbtree<P>::leaf_type *
+mbtree<P>::leftmost_descend_layer(node_base_type *n)
+{
+  node_base_type *cur = n;
+  while (true) {
+    if (cur->isleaf())
+      return static_cast<leaf_type*>(cur);
+    internode_type *in = static_cast<internode_type*>(cur);
+    nodeversion_type version = cur->stable();
+    node_base_type *child = in->child_[0];
+    if (unlikely(in->has_changed(version)))
+      continue;
+    cur = child;
+  }
+}
+
+template <typename P>
+void mbtree<P>::tree_walk(tree_walk_callback &callback) const {
+  rcu_region guard;
+  INVARIANT(rcu::s_instance.in_rcu_region());
+  std::vector<node_base_type *> q, layers;
+  q.push_back(table_.root());
+  while (!q.empty()) {
+    node_base_type *cur = q.back();
+    q.pop_back();
+    prefetch(cur);
+    leaf_type *leaf = leftmost_descend_layer(cur);
+    INVARIANT(leaf);
+    while (leaf) {
+      leaf->prefetch();
+    process:
+      auto version = leaf->stable();
+      auto perm = leaf->permutation();
+      for (int i = 0; i != perm.size(); ++i)
+        if (leaf->is_layer(perm[i]))
+          layers.push_back(leaf->lv_[perm[i]].layer());
+      leaf_type *next = leaf->safe_next();
+      callback.on_node_begin(leaf);
+      if (unlikely(leaf->has_changed(version))) {
+        callback.on_node_failure();
+        layers.clear();
+        goto process;
+      }
+      callback.on_node_success();
+      leaf = next;
+      if (!layers.empty()) {
+        q.insert(q.end(), layers.begin(), layers.end());
+        layers.clear();
+      }
+    }
+  }
+}
+
+template <typename P>
+class mbtree<P>::size_walk_callback : public tree_walk_callback {
+ public:
+  size_walk_callback()
+    : size_(0) {
+  }
+  virtual void on_node_begin(const node_opaque_t *n);
+  virtual void on_node_success();
+  virtual void on_node_failure();
+  size_t size_;
+  int node_size_;
+};
+
+template <typename P>
+void
+mbtree<P>::size_walk_callback::on_node_begin(const node_opaque_t *n)
+{
+  auto perm = n->permutation();
+  node_size_ = 0;
+  for (int i = 0; i != perm.size(); ++i)
+    if (!n->is_layer(perm[i]))
+      ++node_size_;
+}
+
+template <typename P>
+void
+mbtree<P>::size_walk_callback::on_node_success()
+{
+  size_ += node_size_;
+}
+
+template <typename P>
+void
+mbtree<P>::size_walk_callback::on_node_failure()
+{
+}
+
+template <typename P>
+inline size_t mbtree<P>::size() const
+{
+  size_walk_callback c;
+  tree_walk(c);
+  return c.size_;
+}
+
+template <typename P>
+inline bool mbtree<P>::search(const key_type &k, value_type &v,
+                              versioned_node_t *search_info) const
+{
+  rcu_region guard;
+  threadinfo ti;
+  Masstree::unlocked_tcursor<P> lp(table_, k.data(), k.length());
+  bool found = lp.find_unlocked(ti);
+  if (found)
+    v = lp.value();
+  if (search_info)
+    *search_info = versioned_node_t(lp.node(), lp.full_version_value());
+  return found;
+}
+
+template <typename P>
+inline bool mbtree<P>::insert(const key_type &k, value_type v,
+                              value_type *old_v,
+                              insert_info_t *insert_info)
+{
+  rcu_region guard;
+  threadinfo ti;
+  Masstree::tcursor<P> lp(table_, k.data(), k.length());
+  bool found = lp.find_insert(ti);
+  if (!found)
+    ti.advance_timestamp(lp.node_timestamp());
+  if (found && old_v)
+    *old_v = lp.value();
+  lp.value() = v;
+  if (insert_info) {
+    insert_info->node = lp.node();
+    insert_info->old_version = lp.previous_full_version_value();
+    insert_info->new_version = lp.next_full_version_value(1);
+  }
+  lp.finish(1, ti);
+  return !found;
+}
+
+template <typename P>
+inline bool mbtree<P>::insert_if_absent(const key_type &k, value_type v,
+                                        insert_info_t *insert_info)
+{
+  rcu_region guard;
+  threadinfo ti;
+  Masstree::tcursor<P> lp(table_, k.data(), k.length());
+  bool found = lp.find_insert(ti);
+  if (!found) {
+    ti.advance_timestamp(lp.node_timestamp());
+    lp.value() = v;
+    if (insert_info) {
+      insert_info->node = lp.node();
+      insert_info->old_version = lp.previous_full_version_value();
+      insert_info->new_version = lp.next_full_version_value(1);
+    }
+  }
+  lp.finish(!found, ti);
+  return !found;
+}
+
+/**
+ * return true if a value was removed, false otherwise.
+ *
+ * if true and old_v is not NULL, then the removed value of v
+ * is written into old_v
+ */
+template <typename P>
+inline bool mbtree<P>::remove(const key_type &k, value_type *old_v)
+{
+  rcu_region guard;
+  threadinfo ti;
+  Masstree::tcursor<P> lp(table_, k.data(), k.length());
+  bool found = lp.find_locked(ti);
+  if (found && old_v)
+    *old_v = lp.value();
+  lp.finish(found ? -1 : 0, ti);
+  return found;
+}
+
+template <typename P>
+template <bool Reverse>
+class mbtree<P>::search_range_scanner_base {
+ public:
+  search_range_scanner_base(const key_type* boundary)
+    : boundary_(boundary), boundary_compar_(false) {
+  }
+  void check(const Masstree::scanstackelt<P>& iter,
+             const Masstree::key<uint64_t>& key) {
+    int min = std::min(boundary_->length(), key.prefix_length());
+    int cmp = memcmp(boundary_->data(), key.full_string().data(), min);
+    if (!Reverse) {
+      if (cmp < 0 || (cmp == 0 && boundary_->length() <= key.prefix_length()))
+        boundary_compar_ = true;
+      else if (cmp == 0) {
+        uint64_t last_ikey = iter.node()->ikey0_[iter.permutation()[iter.permutation().size() - 1]];
+        boundary_compar_ = boundary_->slice_at(key.prefix_length()) <= last_ikey;
+      }
+    } else {
+      if (cmp >= 0)
+        boundary_compar_ = true;
+    }
+  }
+ protected:
+  const key_type* boundary_;
+  bool boundary_compar_;
+};
+
+template <typename P>
+template <bool Reverse>
+class mbtree<P>::low_level_search_range_scanner
+  : public search_range_scanner_base<Reverse> {
+ public:
+  low_level_search_range_scanner(const key_type* boundary,
+                                 low_level_search_range_callback& callback)
+    : search_range_scanner_base<Reverse>(boundary), callback_(callback) {
+  }
+  void visit_leaf(const Masstree::scanstackelt<P>& iter,
+                  const Masstree::key<uint64_t>& key, threadinfo&) {
+    this->n_ = iter.node();
+    this->v_ = iter.full_version_value();
+    callback_.on_resp_node(this->n_, this->v_);
+    if (this->boundary_)
+      this->check(iter, key);
+  }
+  bool visit_value(const Masstree::key<uint64_t>& key,
+                   value_type value, threadinfo&) {
+    if (this->boundary_compar_) {
+      lcdf::Str bs(this->boundary_->data(), this->boundary_->size());
+      if ((!Reverse && bs <= key.full_string()) ||
+          ( Reverse && bs >= key.full_string()))
+        return false;
+    }
+    return callback_.invoke(key.full_string(), value, this->n_, this->v_);
+  }
+ private:
+  Masstree::leaf<P>* n_;
+  uint64_t v_;
+  low_level_search_range_callback& callback_;
+};
+
+template <typename P>
+template <typename F>
+class mbtree<P>::low_level_search_range_callback_wrapper :
+  public mbtree<P>::low_level_search_range_callback {
+public:
+  low_level_search_range_callback_wrapper(F& callback) : callback_(callback) {}
+
+  void on_resp_node(const node_opaque_t *n, uint64_t version) OVERRIDE {}
+
+  bool
+  invoke(const string_type &k, value_type v,
+         const node_opaque_t *n, uint64_t version) OVERRIDE
+  {
+    return callback_(k, v);
+  }
+
+ private:
+  F& callback_;
+};
+
+template <typename P>
+inline void mbtree<P>::search_range_call(const key_type &lower,
+                                         const key_type *upper,
+                                         low_level_search_range_callback &callback,
+                                         std::string*) const {
+  low_level_search_range_scanner<false> scanner(upper, callback);
+  threadinfo ti;
+  table_.scan(lcdf::Str(lower.data(), lower.length()), true, scanner, ti);
+}
+
+template <typename P>
+inline void mbtree<P>::rsearch_range_call(const key_type &upper,
+                                          const key_type *lower,
+                                          low_level_search_range_callback &callback,
+                                          std::string*) const {
+  low_level_search_range_scanner<true> scanner(lower, callback);
+  threadinfo ti;
+  table_.rscan(lcdf::Str(upper.data(), upper.length()), true, scanner, ti);
+}
+
+template <typename P> template <typename F>
+inline void mbtree<P>::search_range(const key_type &lower,
+                                    const key_type *upper,
+                                    F& callback,
+                                    std::string*) const {
+  low_level_search_range_callback_wrapper<F> wrapper(callback);
+  low_level_search_range_scanner<false> scanner(upper, wrapper);
+  threadinfo ti;
+  table_.scan(lcdf::Str(lower.data(), lower.length()), true, scanner, ti);
+}
+
+template <typename P> template <typename F>
+inline void mbtree<P>::rsearch_range(const key_type &upper,
+                                     const key_type *lower,
+                                     F& callback,
+                                     std::string*) const {
+  low_level_search_range_callback_wrapper<F> wrapper(callback);
+  low_level_search_range_scanner<true> scanner(lower, wrapper);
+  threadinfo ti;
+  table_.rscan(lcdf::Str(upper.data(), upper.length()), true, scanner, ti);
+}
+
+template <typename P>
+std::string mbtree<P>::NodeStringify(const node_opaque_t *n)
+{
+  std::ostringstream b;
+  b << "node[v=" << n->version_value() << "]";
+  return b.str();
+}
+
+template <typename P>
+std::vector<std::pair<typename mbtree<P>::value_type, bool>>
+mbtree<P>::ExtractValues(const node_opaque_t *n)
+{
+  std::vector< std::pair<value_type, bool> > ret;
+  auto perm = n->permutation();
+  for (int i = 0; i != perm.size(); ++i) {
+    int keylenx = n->keylenx_[perm[i]];
+    if (!n->keylenx_is_layer(keylenx))
+      ret.emplace_back(n->lv_[perm[i]].value(), n->keylenx_has_ksuf(keylenx));
+  }
+  return ret;
+}
+
+template <typename P>
+void mbtree<P>::print() {
+  table_.print();
+}
+
+typedef mbtree<masstree_params> concurrent_btree;
+typedef mbtree<masstree_single_threaded_params> single_threaded_btree;
diff --git a/silo/memory.cc b/silo/memory.cc
new file mode 100644 (file)
index 0000000..0516e35
--- /dev/null
@@ -0,0 +1,82 @@
+#include "macros.h" // for TRAP_LARGE_ALLOOCATIONS
+
+#ifdef TRAP_LARGE_ALLOOCATIONS
+
+#include <new>
+#include <iostream>
+#include <stdlib.h>
+#include <stdint.h>
+#include <execinfo.h>
+#include <unistd.h>
+
+static void *
+do_allocation(size_t sz, bool do_throw)
+{
+  if (unlikely(sz > (1 << 20))) { // allocations more than 1MB are suspect
+    // print stacktrace:
+    std::cerr << "Warning: Large memory allocation (" << sz << " bytes)" << std::endl;
+    void *addrs[128];
+    const size_t naddrs = backtrace(addrs, ARRAY_NELEMS(addrs));
+    backtrace_symbols_fd(addrs, naddrs, STDERR_FILENO);
+  }
+  void *ret = malloc(sz);
+  if (unlikely(!ret && do_throw))
+    throw std::bad_alloc();
+  return ret;
+}
+
+static inline void
+do_deletion(void *p)
+{
+  free(p);
+}
+
+void*
+operator new(size_t sz) throw (std::bad_alloc)
+{
+  return do_allocation(sz, true);
+}
+
+void*
+operator new(size_t sz, const std::nothrow_t&) throw ()
+{
+  return do_allocation(sz, false);
+}
+
+void*
+operator new[](size_t sz) throw (std::bad_alloc)
+{
+  return do_allocation(sz, true);
+}
+
+void*
+operator new[](size_t sz, std::nothrow_t &) throw ()
+{
+  return do_allocation(sz, false);
+}
+
+void
+operator delete(void *p) throw ()
+{
+  return do_deletion(p);
+}
+
+void
+operator delete(void *p, const std::nothrow_t &) throw ()
+{
+  return do_deletion(p);
+}
+
+void
+operator delete[](void *p) throw ()
+{
+  return do_deletion(p);
+}
+
+void
+operator delete[](void *p, const std::nothrow_t &) throw ()
+{
+  return do_deletion(p);
+}
+
+#endif
diff --git a/silo/ndb_type_traits.h b/silo/ndb_type_traits.h
new file mode 100644 (file)
index 0000000..34c48bc
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef _NDB_TYPE_TRAITS_H_
+#define _NDB_TYPE_TRAITS_H_
+
+#include <type_traits>
+
+namespace private_ {
+
+  // std::is_trivially_destructible not supported in g++-4.7, so we
+  // do some hacky [conservative] variant of it
+  template <typename T>
+  struct is_trivially_destructible {
+    static const bool value = std::is_scalar<T>::value;
+  };
+
+  template <typename K, typename V>
+  struct is_trivially_destructible<std::pair<K, V>> {
+    static const bool value =
+      is_trivially_destructible<K>::value &&
+      is_trivially_destructible<V>::value;
+  };
+
+  // XXX: same for now
+  template <typename T>
+  struct is_trivially_copyable : public is_trivially_destructible<T> {};
+
+  // user types should add their own specializations
+
+  template <typename T>
+  struct typeutil { typedef const T & func_param_type; };
+
+  template <typename T>
+  struct primitive_typeutil { typedef T func_param_type; };
+
+  // specialize typeutil for int types to use primitive_typeutil
+
+#define SPECIALIZE_PRIM_TYPEUTIL(tpe) \
+  template <> struct typeutil< tpe > : public primitive_typeutil< tpe > {};
+
+  SPECIALIZE_PRIM_TYPEUTIL(bool)
+  SPECIALIZE_PRIM_TYPEUTIL(int8_t)
+  SPECIALIZE_PRIM_TYPEUTIL(uint8_t)
+  SPECIALIZE_PRIM_TYPEUTIL(int16_t)
+  SPECIALIZE_PRIM_TYPEUTIL(uint16_t)
+  SPECIALIZE_PRIM_TYPEUTIL(int32_t)
+  SPECIALIZE_PRIM_TYPEUTIL(uint32_t)
+  SPECIALIZE_PRIM_TYPEUTIL(int64_t)
+  SPECIALIZE_PRIM_TYPEUTIL(uint64_t)
+}
+
+#endif /* _NDB_TYPE_TRAITS_H_ */
diff --git a/silo/new-benchmarks/abstract_db.h b/silo/new-benchmarks/abstract_db.h
new file mode 100644 (file)
index 0000000..e343602
--- /dev/null
@@ -0,0 +1,111 @@
+#ifndef _ABSTRACT_DB_H_
+#define _ABSTRACT_DB_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+
+#include "abstract_ordered_index.h"
+
+class abstract_db {
+public:
+
+  // dtor should close db
+  virtual ~abstract_db() {}
+
+  /**
+   * an approximate max batch size for updates in a transaction.
+   *
+   * A return value of -1 indicates no maximum
+   */
+  virtual ssize_t txn_max_batch_size() const { return -1; }
+
+  /**
+   * XXX(stephentu): hack
+   */
+  virtual void do_txn_epoch_sync() const {}
+
+  /**
+   * XXX(stephentu): hack
+   */
+  virtual void do_txn_finish() const {}
+
+  /** loader should be used as a performance hint, not for correctness */
+  virtual void thread_init(bool loader) {}
+
+  virtual void thread_end() {}
+
+  // [ntxns_persisted, ntxns_committed, avg latency]
+  virtual std::tuple<uint64_t, uint64_t, double>
+    get_ntxn_persisted() const { return std::make_tuple(0, 0, 0.0); }
+
+  virtual void reset_ntxn_persisted() { }
+
+  enum TxnProfileHint {
+    HINT_DEFAULT,
+
+    // ycsb profiles
+    HINT_KV_GET_PUT, // KV workloads over a single key
+    HINT_KV_RMW, // get/put over a single key
+    HINT_KV_SCAN, // KV scan workloads (~100 keys)
+
+    // tpcc profiles
+    HINT_TPCC_NEW_ORDER,
+    HINT_TPCC_PAYMENT,
+    HINT_TPCC_DELIVERY,
+    HINT_TPCC_ORDER_STATUS,
+    HINT_TPCC_ORDER_STATUS_READ_ONLY,
+    HINT_TPCC_STOCK_LEVEL,
+    HINT_TPCC_STOCK_LEVEL_READ_ONLY,
+  };
+
+  ///**
+  // * Initializes a new txn object the space pointed to by buf
+  // *
+  // * Flags is only for the ndb protocol for now
+  // *
+  // * [buf, buf + sizeof_txn_object(txn_flags)) is a valid ptr
+  // */
+  //virtual void *new_txn(uint64_t txn_flags, void *buf,
+  //    TxnProfileHint hint = HINT_DEFAULT) = 0;
+
+  typedef std::map<std::string, uint64_t> counter_map;
+  typedef std::map<std::string, counter_map> txn_counter_map;
+
+  /**
+   * Reports things like read/write set sizes
+   */
+  virtual counter_map
+  get_txn_counters(void *txn) const
+  {
+    return counter_map();
+  }
+
+  ///**
+  // * Returns true on successful commit.
+  // *
+  // * On failure, can either throw abstract_abort_exception, or
+  // * return false- caller should be prepared to deal with both cases
+  // */
+  //virtual bool commit_txn(void *txn) = 0;
+
+  ///**
+  // * XXX
+  // */
+  //virtual void abort_txn(void *txn) = 0;
+
+  //virtual void print_txn_debug(void *txn) const {}
+
+  //virtual abstract_ordered_index *
+  //open_index(const std::string &name,
+  //           size_t value_size_hint,
+  //           bool mostly_append = false) = 0;
+
+  //virtual void
+  //close_index(abstract_ordered_index *idx) = 0;
+};
+
+#endif /* _ABSTRACT_DB_H_ */
diff --git a/silo/new-benchmarks/abstract_ordered_index.h b/silo/new-benchmarks/abstract_ordered_index.h
new file mode 100644 (file)
index 0000000..f05a63d
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef _ABSTRACT_ORDERED_INDEX_H_
+#define _ABSTRACT_ORDERED_INDEX_H_
+
+#include <stdint.h>
+#include <string>
+#include <utility>
+#include <map>
+
+#include "../macros.h"
+#include "../str_arena.h"
+
+class abstract_ordered_index {
+public:
+
+  virtual ~abstract_ordered_index() {}
+
+  // virtual interface
+
+  /**
+   * Only an estimate, not transactional!
+   */
+  virtual size_t size() const = 0;
+
+  /**
+   * Not thread safe for now
+   */
+  virtual std::map<std::string, uint64_t> clear() = 0;
+
+  // templated interface
+
+  // typedef [unspecified] key_type;
+  // typedef [unspecified] value_type;
+
+  // struct search_range_callback {
+  // public:
+  //   virtual bool invoke(const key_type &, const value_type &);
+  // };
+
+  // struct bytes_search_range_callback {
+  // public:
+  //   virtual bool invoke(const std::string &, const std::string &);
+  // };
+
+  //template <typename FieldsMask>
+  //bool search(
+  //    /* [unspecified] */ &t, const key_type &k, value_type &v,
+  //    FieldsMask fm);
+
+  //template <typename FieldsMask>
+  //void search_range_call(
+  //    /* [unspecified] */ &t, const key_type &lower, const key_type *upper,
+  //    search_range_callback &callback,
+  //    bool no_key_results, /* skip decoding of keys? */
+  //    FieldsMask fm);
+
+  //void bytes_search_range_call(
+  //    /* [unspecified] */ &t, const key_type &lower, const key_type *upper,
+  //    bytes_search_range_callback &callback,
+  //    size_t value_fields_prefix);
+
+  //template <typename FieldsMask>
+  //void put(
+  //    /* [unspecified] */ &t, const key_type &k, const value_type &v,
+  //    FieldsMask fm);
+
+  //template <typename Traits>
+  //void insert(
+  //    /* [unspecified] */ &t, const key_type &k, const value_type &v);
+
+  //template <typename Traits>
+  //void remove(
+  //    /* [unspecified] */ &t, const key_type &k);
+};
+
+#endif /* _ABSTRACT_ORDERED_INDEX_H_ */
diff --git a/silo/new-benchmarks/bench.cc b/silo/new-benchmarks/bench.cc
new file mode 100644 (file)
index 0000000..a027711
--- /dev/null
@@ -0,0 +1,406 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <utility>
+#include <string>
+
+#include <stdlib.h>
+#include <sched.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#include "bench.h"
+
+#include "../counter.h"
+#include "../scopedperf.hh"
+#include "../allocator.h"
+
+#ifdef USE_JEMALLOC
+//cannot include this header b/c conflicts with malloc.h
+//#include <jemalloc/jemalloc.h>
+extern "C" void malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque, const char *opts);
+extern "C" int mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
+#endif
+#ifdef USE_TCMALLOC
+#include <google/heap-profiler.h>
+#endif
+
+using namespace std;
+using namespace util;
+
+size_t nthreads = 1;
+volatile bool running = true;
+int verbose = 0;
+uint64_t txn_flags = 0;
+double scale_factor = 1.0;
+uint64_t runtime = 30;
+uint64_t ops_per_worker = 0;
+int run_mode = RUNMODE_TIME;
+int enable_parallel_loading = false;
+int pin_cpus = 0;
+int slow_exit = 0;
+int retry_aborted_transaction = 0;
+int no_reset_counters = 0;
+int backoff_aborted_transaction = 0;
+
+template <typename T>
+static vector<T>
+elemwise_sum(const vector<T> &a, const vector<T> &b)
+{
+  INVARIANT(a.size() == b.size());
+  vector<T> ret(a.size());
+  for (size_t i = 0; i < a.size(); i++)
+    ret[i] = a[i] + b[i];
+  return ret;
+}
+
+template <typename K, typename V>
+static void
+map_agg(map<K, V> &agg, const map<K, V> &m)
+{
+  for (typename map<K, V>::const_iterator it = m.begin();
+       it != m.end(); ++it)
+    agg[it->first] += it->second;
+}
+
+// returns <free_bytes, total_bytes>
+static pair<uint64_t, uint64_t>
+get_system_memory_info()
+{
+  struct sysinfo inf;
+  sysinfo(&inf);
+  return make_pair(inf.mem_unit * inf.freeram, inf.mem_unit * inf.totalram);
+}
+
+static bool
+clear_file(const char *name)
+{
+  ofstream ofs(name);
+  ofs.close();
+  return true;
+}
+
+static void
+write_cb(void *p, const char *s) UNUSED;
+static void
+write_cb(void *p, const char *s)
+{
+  const char *f = "jemalloc.stats";
+  static bool s_clear_file UNUSED = clear_file(f);
+  ofstream ofs(f, ofstream::app);
+  ofs << s;
+  ofs.flush();
+  ofs.close();
+}
+
+static event_avg_counter evt_avg_abort_spins("avg_abort_spins");
+
+void
+bench_worker::run()
+{
+  // XXX(stephentu): so many nasty hacks here. should actually
+  // fix some of this stuff one day
+  if (set_core_id)
+    coreid::set_core_id(worker_id); // cringe
+  {
+    scoped_rcu_region r; // register this thread in rcu region
+  }
+  on_run_setup();
+  scoped_db_thread_ctx ctx(db, false);
+  const workload_desc_vec workload = get_workload();
+  txn_counts.resize(workload.size());
+  barrier_a->count_down();
+  barrier_b->wait_for();
+  while (running && (run_mode != RUNMODE_OPS || ntxn_commits < ops_per_worker)) {
+    double d = r.next_uniform();
+    for (size_t i = 0; i < workload.size(); i++) {
+      if ((i + 1) == workload.size() || d < workload[i].frequency) {
+      retry:
+        timer t;
+        const unsigned long old_seed = r.get_seed();
+        const auto ret = workload[i].fn(this);
+        if (likely(ret.first)) {
+          ++ntxn_commits;
+          latency_numer_us += t.lap();
+          backoff_shifts >>= 1;
+        } else {
+          ++ntxn_aborts;
+          if (retry_aborted_transaction && running) {
+            if (backoff_aborted_transaction) {
+              if (backoff_shifts < 63)
+                backoff_shifts++;
+              uint64_t spins = 1UL << backoff_shifts;
+              spins *= 100000; // XXX: tuned pretty arbitrarily
+              evt_avg_abort_spins.offer(spins);
+              while (spins) {
+                nop_pause();
+                spins--;
+              }
+            }
+            r.set_seed(old_seed);
+            goto retry;
+          }
+        }
+        size_delta += ret.second; // should be zero on abort
+        txn_counts[i]++; // txn_counts aren't used to compute throughput (is
+                         // just an informative number to print to the console
+                         // in verbose mode)
+        break;
+      }
+      d -= workload[i].frequency;
+    }
+  }
+}
+
+void
+bench_runner::run()
+{
+  // load data
+  const vector<unique_ptr<bench_loader>> loaders = make_loaders();
+  {
+    spin_barrier b(loaders.size());
+    const pair<uint64_t, uint64_t> mem_info_before = get_system_memory_info();
+    {
+      scoped_timer t("dataloading", verbose);
+      for (auto &p : loaders) {
+        p->set_barrier(b);
+        p->start();
+      }
+      for (auto &p : loaders)
+        p->join();
+    }
+    const pair<uint64_t, uint64_t> mem_info_after = get_system_memory_info();
+    const int64_t delta = int64_t(mem_info_before.first) - int64_t(mem_info_after.first); // free mem
+    const double delta_mb = double(delta)/1048576.0;
+    if (verbose)
+      cerr << "DB size: " << delta_mb << " MB" << endl;
+  }
+
+  db->do_txn_epoch_sync(); // also waits for worker threads to be persisted
+  {
+    const auto persisted_info = db->get_ntxn_persisted();
+    if (get<0>(persisted_info) != get<1>(persisted_info))
+      cerr << "ERROR: " << persisted_info << endl;
+    //ALWAYS_ASSERT(get<0>(persisted_info) == get<1>(persisted_info));
+    if (verbose)
+      cerr << persisted_info << " txns persisted in loading phase" << endl;
+  }
+  db->reset_ntxn_persisted();
+
+  if (!no_reset_counters) {
+    event_counter::reset_all_counters(); // XXX: for now - we really should have a before/after loading
+    PERF_EXPR(scopedperf::perfsum_base::resetall());
+  }
+  {
+    const auto persisted_info = db->get_ntxn_persisted();
+    if (get<0>(persisted_info) != 0 ||
+        get<1>(persisted_info) != 0 ||
+        get<2>(persisted_info) != 0.0) {
+      cerr << persisted_info << endl;
+      ALWAYS_ASSERT(false);
+    }
+  }
+
+  map<string, size_t> table_sizes_before;
+  if (verbose) {
+    for (auto &p : open_tables) {
+      scoped_rcu_region guard;
+      const size_t s = p.second->size();
+      cerr << "table " << p.first << " size " << s << endl;
+      table_sizes_before[p.first] = s;
+    }
+    cerr << "starting benchmark..." << endl;
+  }
+
+  const pair<uint64_t, uint64_t> mem_info_before = get_system_memory_info();
+
+  const vector<unique_ptr<bench_worker>> workers = make_workers();
+  ALWAYS_ASSERT(!workers.empty());
+  for (auto &p : workers)
+    p->start();
+
+  barrier_a.wait_for(); // wait for all threads to start up
+  timer t, t_nosync;
+  barrier_b.count_down(); // bombs away!
+  if (run_mode == RUNMODE_TIME) {
+    sleep(runtime);
+    running = false;
+  }
+  __sync_synchronize();
+  for (size_t i = 0; i < nthreads; i++)
+    workers[i]->join();
+  const unsigned long elapsed_nosync = t_nosync.lap();
+  db->do_txn_finish(); // waits for all worker txns to persist
+  size_t n_commits = 0;
+  size_t n_aborts = 0;
+  uint64_t latency_numer_us = 0;
+  for (size_t i = 0; i < nthreads; i++) {
+    n_commits += workers[i]->get_ntxn_commits();
+    n_aborts += workers[i]->get_ntxn_aborts();
+    latency_numer_us += workers[i]->get_latency_numer_us();
+  }
+  const auto persisted_info = db->get_ntxn_persisted();
+
+  const unsigned long elapsed = t.lap(); // lap() must come after do_txn_finish(),
+                                         // because do_txn_finish() potentially
+                                         // waits a bit
+
+  // various sanity checks
+  ALWAYS_ASSERT(get<0>(persisted_info) == get<1>(persisted_info));
+  // not == b/c persisted_info does not count read-only txns
+  ALWAYS_ASSERT(n_commits >= get<1>(persisted_info));
+
+  const double elapsed_nosync_sec = double(elapsed_nosync) / 1000000.0;
+  const double agg_nosync_throughput = double(n_commits) / elapsed_nosync_sec;
+  const double avg_nosync_per_core_throughput = agg_nosync_throughput / double(workers.size());
+
+  const double elapsed_sec = double(elapsed) / 1000000.0;
+  const double agg_throughput = double(n_commits) / elapsed_sec;
+  const double avg_per_core_throughput = agg_throughput / double(workers.size());
+
+  const double agg_abort_rate = double(n_aborts) / elapsed_sec;
+  const double avg_per_core_abort_rate = agg_abort_rate / double(workers.size());
+
+  // we can use n_commits here, because we explicitly wait for all txns
+  // run to be durable
+  const double agg_persist_throughput = double(n_commits) / elapsed_sec;
+  const double avg_per_core_persist_throughput =
+    agg_persist_throughput / double(workers.size());
+
+  // XXX(stephentu): latency currently doesn't account for read-only txns
+  const double avg_latency_us =
+    double(latency_numer_us) / double(n_commits);
+  const double avg_latency_ms = avg_latency_us / 1000.0;
+  const double avg_persist_latency_ms =
+    get<2>(persisted_info) / 1000.0;
+
+  if (verbose) {
+    const pair<uint64_t, uint64_t> mem_info_after = get_system_memory_info();
+    const int64_t delta = int64_t(mem_info_before.first) - int64_t(mem_info_after.first); // free mem
+    const double delta_mb = double(delta)/1048576.0;
+    map<string, size_t> agg_txn_counts = workers[0]->get_txn_counts();
+    ssize_t size_delta = workers[0]->get_size_delta();
+    for (size_t i = 1; i < workers.size(); i++) {
+      map_agg(agg_txn_counts, workers[i]->get_txn_counts());
+      size_delta += workers[i]->get_size_delta();
+    }
+    const double size_delta_mb = double(size_delta)/1048576.0;
+    map<string, counter_data> ctrs = event_counter::get_all_counters();
+
+    cerr << "--- table statistics ---" << endl;
+    for (auto &p : open_tables) {
+      scoped_rcu_region guard;
+      const size_t s = p.second->size();
+      const ssize_t delta = ssize_t(s) - ssize_t(table_sizes_before[p.first]);
+      cerr << "table " << p.first << " size " << p.second->size();
+      if (delta < 0)
+        cerr << " (" << delta << " records)" << endl;
+      else
+        cerr << " (+" << delta << " records)" << endl;
+    }
+#ifdef ENABLE_BENCH_TXN_COUNTERS
+    cerr << "--- txn counter statistics ---" << endl;
+    {
+      // take from thread 0 for now
+      abstract_db::txn_counter_map agg = workers[0]->get_local_txn_counters();
+      for (auto &p : agg) {
+        cerr << p.first << ":" << endl;
+        for (auto &q : p.second)
+          cerr << "  " << q.first << " : " << q.second << endl;
+      }
+    }
+#endif
+    cerr << "--- benchmark statistics ---" << endl;
+    cerr << "runtime: " << elapsed_sec << " sec" << endl;
+    cerr << "memory delta: " << delta_mb  << " MB" << endl;
+    cerr << "memory delta rate: " << (delta_mb / elapsed_sec)  << " MB/sec" << endl;
+    cerr << "logical memory delta: " << size_delta_mb << " MB" << endl;
+    cerr << "logical memory delta rate: " << (size_delta_mb / elapsed_sec) << " MB/sec" << endl;
+    cerr << "agg_nosync_throughput: " << agg_nosync_throughput << " ops/sec" << endl;
+    cerr << "avg_nosync_per_core_throughput: " << avg_nosync_per_core_throughput << " ops/sec/core" << endl;
+    cerr << "agg_throughput: " << agg_throughput << " ops/sec" << endl;
+    cerr << "avg_per_core_throughput: " << avg_per_core_throughput << " ops/sec/core" << endl;
+    cerr << "agg_persist_throughput: " << agg_persist_throughput << " ops/sec" << endl;
+    cerr << "avg_per_core_persist_throughput: " << avg_per_core_persist_throughput << " ops/sec/core" << endl;
+    cerr << "avg_latency: " << avg_latency_ms << " ms" << endl;
+    cerr << "avg_persist_latency: " << avg_persist_latency_ms << " ms" << endl;
+    cerr << "agg_abort_rate: " << agg_abort_rate << " aborts/sec" << endl;
+    cerr << "avg_per_core_abort_rate: " << avg_per_core_abort_rate << " aborts/sec/core" << endl;
+    cerr << "txn breakdown: " << format_list(agg_txn_counts.begin(), agg_txn_counts.end()) << endl;
+    cerr << "--- system counters (for benchmark) ---" << endl;
+    for (map<string, counter_data>::iterator it = ctrs.begin();
+         it != ctrs.end(); ++it)
+      cerr << it->first << ": " << it->second << endl;
+    cerr << "--- perf counters (if enabled, for benchmark) ---" << endl;
+    PERF_EXPR(scopedperf::perfsum_base::printall());
+    cerr << "--- allocator stats ---" << endl;
+    ::allocator::DumpStats();
+    cerr << "---------------------------------------" << endl;
+
+#ifdef USE_JEMALLOC
+    cerr << "dumping heap profile..." << endl;
+    mallctl("prof.dump", NULL, NULL, NULL, 0);
+    cerr << "printing jemalloc stats..." << endl;
+    malloc_stats_print(write_cb, NULL, "");
+#endif
+#ifdef USE_TCMALLOC
+    HeapProfilerDump("before-exit");
+#endif
+  }
+
+  // output for plotting script
+  cout << agg_throughput << " "
+       << agg_persist_throughput << " "
+       << avg_latency_ms << " "
+       << avg_persist_latency_ms << " "
+       << agg_abort_rate << endl;
+  cout.flush();
+
+  if (!slow_exit)
+    exit(0); // exit() instead of returning, so we don't call a bunch of dtors
+
+  map<string, uint64_t> agg_stats;
+  for (auto &p : open_tables)
+    map_agg(agg_stats, p.second->clear());
+  if (verbose)
+    for (auto &p : agg_stats)
+      cerr << p.first << " : " << p.second << endl;
+}
+
+template <typename K, typename V>
+struct map_maxer {
+  typedef map<K, V> map_type;
+  void
+  operator()(map_type &agg, const map_type &m) const
+  {
+    for (typename map_type::const_iterator it = m.begin();
+        it != m.end(); ++it)
+      agg[it->first] = std::max(agg[it->first], it->second);
+  }
+};
+
+//template <typename KOuter, typename KInner, typename VInner>
+//struct map_maxer<KOuter, map<KInner, VInner>> {
+//  typedef map<KInner, VInner> inner_map_type;
+//  typedef map<KOuter, inner_map_type> map_type;
+//};
+
+//#ifdef ENABLE_BENCH_TXN_COUNTERS
+//void
+//bench_worker::measure_txn_counters(void *txn, const char *txn_name)
+//{
+//  auto ret = db->get_txn_counters(txn);
+//  map_maxer<string, uint64_t>()(local_txn_counters[txn_name], ret);
+//}
+//#endif
+
+map<string, size_t>
+bench_worker::get_txn_counts() const
+{
+  map<string, size_t> m;
+  const workload_desc_vec workload = get_workload();
+  for (size_t i = 0; i < txn_counts.size(); i++)
+    m[workload[i].name] = txn_counts[i];
+  return m;
+}
diff --git a/silo/new-benchmarks/bench.h b/silo/new-benchmarks/bench.h
new file mode 100644 (file)
index 0000000..3c62e9f
--- /dev/null
@@ -0,0 +1,422 @@
+#ifndef _NDB_BENCH_H_
+#define _NDB_BENCH_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+#include <utility>
+#include <string>
+
+#include "abstract_db.h"
+#include "../macros.h"
+#include "../thread.h"
+#include "../util.h"
+#include "../spinbarrier.h"
+#include "../rcu.h"
+
+struct persistconfig {
+  persistconfig()
+    : nofsync_(0), do_compress_(0), fake_writes_(0),
+      disable_gc_(0), disable_snapshots_(0) {}
+  int nofsync_;
+  int do_compress_;
+  int fake_writes_;
+  int disable_gc_;
+  int disable_snapshots_;
+  std::vector<std::string> logfiles_;
+  std::vector<std::vector<unsigned>> assignments_;
+};
+
+extern void tpcc_do_test(
+    const std::string &dbtype,
+    const persistconfig &cfg,
+    int argc, char **argv);
+
+enum { RUNMODE_TIME = 0,
+       RUNMODE_OPS  = 1};
+
+// benchmark global variables
+extern size_t nthreads;
+extern volatile bool running;
+extern int verbose;
+extern uint64_t txn_flags;
+extern double scale_factor;
+extern uint64_t runtime;
+extern uint64_t ops_per_worker;
+extern int run_mode;
+extern int enable_parallel_loading;
+extern int pin_cpus;
+extern int slow_exit;
+extern int retry_aborted_transaction;
+extern int no_reset_counters;
+extern int backoff_aborted_transaction;
+
+// NOTE: the typed_* versions of classes exist so we don't have to convert all
+// classes to templatetized [for sanity in compliation times]; we trade off
+// a bit of type-safety for more rapid development cycles
+
+class scoped_db_thread_ctx {
+public:
+  scoped_db_thread_ctx(const scoped_db_thread_ctx &) = delete;
+  scoped_db_thread_ctx(scoped_db_thread_ctx &&) = delete;
+  scoped_db_thread_ctx &operator=(const scoped_db_thread_ctx &) = delete;
+
+  scoped_db_thread_ctx(abstract_db *db, bool loader)
+    : db(db)
+  {
+    db->thread_init(loader);
+  }
+  ~scoped_db_thread_ctx()
+  {
+    db->thread_end();
+  }
+private:
+  abstract_db *const db;
+};
+
+class bench_loader : public ndb_thread {
+public:
+  bench_loader(unsigned long seed, abstract_db *db)
+    : r(seed), db(db), b(0)
+  {
+  }
+  inline void
+  set_barrier(spin_barrier &b)
+  {
+    ALWAYS_ASSERT(!this->b);
+    this->b = &b;
+  }
+  virtual void
+  run()
+  {
+    { // XXX(stephentu): this is a hack
+      scoped_rcu_region r; // register this thread in rcu region
+    }
+    ALWAYS_ASSERT(b);
+    b->count_down();
+    b->wait_for();
+    scoped_db_thread_ctx ctx(db, true);
+    load();
+  }
+protected:
+
+  virtual void load() = 0;
+
+  util::fast_random r;
+  abstract_db *const db;
+  spin_barrier *b;
+  str_arena arena;
+};
+
+template <typename Database>
+class typed_bench_loader : public bench_loader {
+public:
+  typed_bench_loader(unsigned long seed, Database *db)
+    : bench_loader(seed, db) {}
+  inline Database *
+  typed_db()
+  {
+    return static_cast<Database *>(db);
+  }
+  inline const Database *
+  typed_db() const
+  {
+    return static_cast<const Database *>(db);
+  }
+};
+
+class bench_worker : public ndb_thread {
+public:
+
+  bench_worker(unsigned int worker_id,
+               bool set_core_id,
+               unsigned long seed, abstract_db *db,
+               spin_barrier *barrier_a, spin_barrier *barrier_b)
+    : worker_id(worker_id), set_core_id(set_core_id),
+      r(seed), db(db),
+      barrier_a(barrier_a), barrier_b(barrier_b),
+      // the ntxn_* numbers are per worker
+      ntxn_commits(0), ntxn_aborts(0),
+      latency_numer_us(0),
+      backoff_shifts(0), // spin between [0, 2^backoff_shifts) times before retry
+      size_delta(0)
+  {
+  }
+
+  virtual ~bench_worker() {}
+
+  // returns [did_commit?, size_increase_bytes]
+  typedef std::pair<bool, ssize_t> txn_result;
+  typedef txn_result (*txn_fn_t)(bench_worker *);
+
+  struct workload_desc {
+    workload_desc() {}
+    workload_desc(const std::string &name, double frequency, txn_fn_t fn)
+      : name(name), frequency(frequency), fn(fn)
+    {
+      ALWAYS_ASSERT(frequency > 0.0);
+      ALWAYS_ASSERT(frequency <= 1.0);
+    }
+    std::string name;
+    double frequency;
+    txn_fn_t fn;
+  };
+  typedef std::vector<workload_desc> workload_desc_vec;
+  virtual workload_desc_vec get_workload() const = 0;
+
+  virtual void run();
+
+  inline size_t get_ntxn_commits() const { return ntxn_commits; }
+  inline size_t get_ntxn_aborts() const { return ntxn_aborts; }
+
+  inline uint64_t get_latency_numer_us() const { return latency_numer_us; }
+
+  inline double
+  get_avg_latency_us() const
+  {
+    return double(latency_numer_us) / double(ntxn_commits);
+  }
+
+  std::map<std::string, size_t> get_txn_counts() const;
+
+  typedef abstract_db::counter_map counter_map;
+  typedef abstract_db::txn_counter_map txn_counter_map;
+
+#ifdef ENABLE_BENCH_TXN_COUNTERS
+  inline txn_counter_map
+  get_local_txn_counters() const
+  {
+    return local_txn_counters;
+  }
+#endif
+
+  inline ssize_t get_size_delta() const { return size_delta; }
+
+protected:
+
+  virtual void on_run_setup() {}
+
+  unsigned int worker_id;
+  bool set_core_id;
+  util::fast_random r;
+  abstract_db *const db;
+  spin_barrier *const barrier_a;
+  spin_barrier *const barrier_b;
+
+private:
+  size_t ntxn_commits;
+  size_t ntxn_aborts;
+  uint64_t latency_numer_us;
+  unsigned backoff_shifts;
+
+protected:
+
+//#ifdef ENABLE_BENCH_TXN_COUNTERS
+//  txn_counter_map local_txn_counters;
+//  void measure_txn_counters(void *txn, const char *txn_name);
+//#else
+//  inline ALWAYS_INLINE void measure_txn_counters(void *txn, const char *txn_name) {}
+//#endif
+
+  std::vector<size_t> txn_counts; // breakdown of txns
+  ssize_t size_delta; // how many logical bytes (of values) did the worker add to the DB
+
+  str_arena arena;
+};
+
+class bench_runner {
+public:
+  bench_runner(const bench_runner &) = delete;
+  bench_runner(bench_runner &&) = delete;
+  bench_runner &operator=(const bench_runner &) = delete;
+
+  bench_runner(abstract_db *db)
+    : db(db), barrier_a(nthreads), barrier_b(1) {}
+  virtual ~bench_runner() {}
+  void run();
+protected:
+  // only called once
+  virtual std::vector<std::unique_ptr<bench_loader>> make_loaders() = 0;
+
+  // only called once
+  virtual std::vector<std::unique_ptr<bench_worker>> make_workers() = 0;
+
+  abstract_db *const db;
+  std::map<std::string, std::shared_ptr<abstract_ordered_index>> open_tables;
+
+  // barriers for actual benchmark execution
+  spin_barrier barrier_a;
+  spin_barrier barrier_b;
+};
+
+template <typename Database>
+class typed_bench_runner : public bench_runner {
+public:
+  typed_bench_runner(Database *db)
+    : bench_runner(db) {}
+  inline Database *
+  typed_db()
+  {
+    return static_cast<Database *>(db);
+  }
+  inline const Database *
+  typed_db() const
+  {
+    return static_cast<const Database *>(db);
+  }
+};
+
+template <typename Index>
+class latest_key_callback : public Index::bytes_search_range_callback {
+public:
+
+  latest_key_callback(std::string &k, ssize_t limit = -1)
+    : limit(limit), n(0), k(&k)
+  {
+    ALWAYS_ASSERT(limit == -1 || limit > 0);
+  }
+
+  virtual bool invoke(
+      const std::string &key,
+      const std::string &value)
+  {
+    INVARIANT(limit == -1 || n < size_t(limit));
+    // see the note in bytes_static_limit_callback for why we explicitly
+    // copy over regular (ref-counting) assignment
+    k->assign(key.data(), key.size());
+    ++n;
+    return (limit == -1) || (n < size_t(limit));
+  }
+
+  inline size_t size() const { return n; }
+  inline std::string &kstr() { return *k; }
+
+private:
+  ssize_t limit;
+  size_t n;
+  std::string *k;
+};
+
+namespace private_ {
+  template <typename T, bool enable>
+  struct container {
+    container() {}
+    container(const T &t) {}
+    T & get(); // not defined
+  };
+
+  template <typename T>
+  struct container<T, true> {
+    container() {}
+    container(const T &t) : t(t) {}
+    inline T & get() { return t; }
+    T t;
+  };
+}
+
+// explicitly copies keys, because btree::search_range_call() interally
+// re-uses a single string to pass keys (so using standard string assignment
+// will force a re-allocation b/c of shared ref-counting)
+//
+// this isn't done for values, because each value has a distinct string from
+// the string allocator, so there are no mutations while holding > 1 ref-count
+template <typename Index, size_t N, bool ignore_key>
+class bytes_static_limit_callback : public Index::bytes_search_range_callback {
+public:
+
+  static_assert(N > 0, "xx");
+
+  bytes_static_limit_callback(str_arena *arena)
+    : arena(arena)
+  {
+  }
+
+  virtual bool invoke(
+      const std::string &key,
+      const std::string &value) OVERRIDE
+  {
+    INVARIANT(size() < N);
+    INVARIANT(arena->manages(&key));
+    INVARIANT(arena->manages(&value));
+    if (ignore_key) {
+      values.emplace_back(nullptr, &value);
+    } else {
+      // see note above
+      std::string * const s_px = arena->next();
+      INVARIANT(s_px && s_px->empty());
+      s_px->assign(key.data(), key.size());
+      values.emplace_back(s_px, &value);
+    }
+    return size() < N;
+  }
+
+  inline size_t
+  size() const
+  {
+    return values.size();
+  }
+
+  inline const std::string &
+  key(size_t i) const
+  {
+    return *values[i].first.get();
+  }
+
+  inline const std::string &
+  value(size_t i) const
+  {
+    return *values[i].second;
+  }
+
+private:
+  typedef std::pair<
+    private_::container<const std::string *, !ignore_key>,
+    const std::string *> kv_pair;
+  typename util::vec<kv_pair, N>::type values;
+  str_arena *arena;
+};
+
+template <typename Index, size_t N, bool ignore_key>
+class static_limit_callback : public Index::search_range_callback {
+public:
+
+  static_assert(N > 0, "xx");
+
+  virtual bool
+  invoke(
+      const typename Index::key_type &key,
+      const typename Index::value_type &value) OVERRIDE
+  {
+    INVARIANT(size() < N);
+    values.emplace_back(key, value);
+    return size() < N;
+  }
+
+  inline size_t
+  size() const
+  {
+    return values.size();
+  }
+
+  inline typename Index::key_type &
+  key(size_t i)
+  {
+    return values[i].first.get();
+  }
+
+  inline typename Index::value_type &
+  value(size_t i)
+  {
+    return values[i].second;
+  }
+
+private:
+  typedef std::pair<
+    private_::container<typename Index::key_type, !ignore_key>,
+    typename Index::value_type> kv_pair;
+  typename util::vec<kv_pair, N>::type values;
+};
+
+#endif /* _NDB_BENCH_H_ */
diff --git a/silo/new-benchmarks/dbtest.cc b/silo/new-benchmarks/dbtest.cc
new file mode 100644 (file)
index 0000000..0c1d5b6
--- /dev/null
@@ -0,0 +1,340 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <utility>
+#include <string>
+#include <set>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#include "../allocator.h"
+#include "../stats_server.h"
+#include "../btree_choice.h"
+#include "bench.h"
+
+using namespace std;
+using namespace util;
+
+static vector<string>
+split_ws(const string &s)
+{
+  vector<string> r;
+  istringstream iss(s);
+  copy(istream_iterator<string>(iss),
+       istream_iterator<string>(),
+       back_inserter<vector<string>>(r));
+  return r;
+}
+
+static size_t
+parse_memory_spec(const string &s)
+{
+  string x(s);
+  size_t mult = 1;
+  if (x.back() == 'G') {
+    mult = static_cast<size_t>(1) << 30;
+    x.pop_back();
+  } else if (x.back() == 'M') {
+    mult = static_cast<size_t>(1) << 20;
+    x.pop_back();
+  } else if (x.back() == 'K') {
+    mult = static_cast<size_t>(1) << 10;
+    x.pop_back();
+  }
+  return strtoul(x.c_str(), nullptr, 10) * mult;
+}
+
+int
+main(int argc, char **argv)
+{
+  void (*test_fn)(const string &, const persistconfig &, int, char **) = NULL;
+  string bench_type = "ycsb";
+  string db_type = "ndb-proto2";
+  char *curdir = get_current_dir_name();
+  string basedir = curdir;
+  string bench_opts;
+  size_t numa_memory = 0;
+  free(curdir);
+  int saw_run_spec = 0;
+  persistconfig cfg;
+  string stats_server_sockfile;
+  while (1) {
+    static struct option long_options[] =
+    {
+      {"verbose"                    , no_argument       , &verbose                   , 1}   ,
+      {"parallel-loading"           , no_argument       , &enable_parallel_loading   , 1}   ,
+      {"pin-cpus"                   , no_argument       , &pin_cpus                  , 1}   ,
+      {"slow-exit"                  , no_argument       , &slow_exit                 , 1}   ,
+      {"retry-aborted-transactions" , no_argument       , &retry_aborted_transaction , 1}   ,
+      {"backoff-aborted-transactions" , no_argument     , &backoff_aborted_transaction , 1}   ,
+      {"bench"                      , required_argument , 0                          , 'b'} ,
+      {"scale-factor"               , required_argument , 0                          , 's'} ,
+      {"num-threads"                , required_argument , 0                          , 't'} ,
+      {"db-type"                    , required_argument , 0                          , 'd'} ,
+      {"basedir"                    , required_argument , 0                          , 'B'} ,
+      {"txn-flags"                  , required_argument , 0                          , 'f'} ,
+      {"runtime"                    , required_argument , 0                          , 'r'} ,
+      {"ops-per-worker"             , required_argument , 0                          , 'n'} ,
+      {"bench-opts"                 , required_argument , 0                          , 'o'} ,
+      {"numa-memory"                , required_argument , 0                          , 'm'} , // implies --pin-cpus
+      {"logfile"                    , required_argument , 0                          , 'l'} ,
+      {"assignment"                 , required_argument , 0                          , 'a'} ,
+      {"log-nofsync"                , no_argument       , &cfg.nofsync_              , 1}   ,
+      {"log-compress"               , no_argument       , &cfg.do_compress_          , 1}   ,
+      {"log-fake-writes"            , no_argument       , &cfg.fake_writes_          , 1}   ,
+      {"disable-gc"                 , no_argument       , &cfg.disable_gc_           , 1}   ,
+      {"disable-snapshots"          , no_argument       , &cfg.disable_snapshots_    , 1}   ,
+      {"stats-server-sockfile"      , required_argument , 0                          , 'x'} ,
+      {"no-reset-counters"          , no_argument       , &no_reset_counters         , 1}   ,
+      {0, 0, 0, 0}
+    };
+    int option_index = 0;
+    int c = getopt_long(argc, argv, "b:s:t:d:B:f:r:n:o:m:l:a:x:", long_options, &option_index);
+    if (c == -1)
+      break;
+
+    switch (c) {
+    case 0:
+      if (long_options[option_index].flag != 0)
+        break;
+      abort();
+      break;
+
+    case 'b':
+      bench_type = optarg;
+      break;
+
+    case 's':
+      scale_factor = strtod(optarg, NULL);
+      ALWAYS_ASSERT(scale_factor > 0.0);
+      break;
+
+    case 't':
+      nthreads = strtoul(optarg, NULL, 10);
+      ALWAYS_ASSERT(nthreads > 0);
+      break;
+
+    case 'd':
+      db_type = optarg;
+      break;
+
+    case 'B':
+      basedir = optarg;
+      break;
+
+    case 'f':
+      txn_flags = strtoul(optarg, NULL, 10);
+      break;
+
+    case 'r':
+      ALWAYS_ASSERT(!saw_run_spec);
+      saw_run_spec = 1;
+      runtime = strtoul(optarg, NULL, 10);
+      ALWAYS_ASSERT(runtime > 0);
+      run_mode = RUNMODE_TIME;
+      break;
+
+    case 'n':
+      ALWAYS_ASSERT(!saw_run_spec);
+      saw_run_spec = 1;
+      ops_per_worker = strtoul(optarg, NULL, 10);
+      ALWAYS_ASSERT(ops_per_worker > 0);
+      run_mode = RUNMODE_OPS;
+
+    case 'o':
+      bench_opts = optarg;
+      break;
+
+    case 'm':
+      {
+        pin_cpus = 1;
+        const size_t m = parse_memory_spec(optarg);
+        ALWAYS_ASSERT(m > 0);
+        numa_memory = m;
+      }
+      break;
+
+    case 'l':
+      cfg.logfiles_.emplace_back(optarg);
+      break;
+
+    case 'a':
+      cfg.assignments_.emplace_back(
+          ParseCSVString<unsigned, RangeAwareParser<unsigned>>(optarg));
+      break;
+
+    case 'x':
+      stats_server_sockfile = optarg;
+      break;
+
+    case '?':
+      /* getopt_long already printed an error message. */
+      exit(1);
+
+    default:
+      abort();
+    }
+  }
+
+  if (bench_type == "tpcc")
+    test_fn = tpcc_do_test;
+  else
+    ALWAYS_ASSERT(false);
+
+  if (cfg.do_compress_ && cfg.logfiles_.empty()) {
+    cerr << "[ERROR] --log-compress specified without logging enabled" << endl;
+    return 1;
+  }
+
+  if (cfg.fake_writes_ && cfg.logfiles_.empty()) {
+    cerr << "[ERROR] --log-fake-writes specified without logging enabled" << endl;
+    return 1;
+  }
+
+  if (cfg.nofsync_ && cfg.logfiles_.empty()) {
+    cerr << "[ERROR] --log-nofsync specified without logging enabled" << endl;
+    return 1;
+  }
+
+  if (cfg.fake_writes_ && cfg.nofsync_) {
+    cerr << "[WARNING] --log-nofsync has no effect with --log-fake-writes enabled" << endl;
+  }
+
+#ifndef ENABLE_EVENT_COUNTERS
+  if (!stats_server_sockfile.empty()) {
+    cerr << "[WARNING] --stats-server-sockfile with no event counters enabled is useless" << endl;
+  }
+#endif
+
+  // initialize the numa allocator
+  if (numa_memory > 0) {
+    const size_t maxpercpu = iceil(
+        numa_memory / nthreads, ::allocator::GetHugepageSize());
+    numa_memory = maxpercpu * nthreads;
+    ::allocator::Initialize(nthreads, maxpercpu);
+  }
+
+  const set<string> can_persist({"ndb-proto2"});
+  if (!cfg.logfiles_.empty() && !can_persist.count(db_type)) {
+    cerr << "[ERROR] benchmark " << db_type
+         << " does not have persistence implemented" << endl;
+    return 1;
+  }
+
+#ifdef PROTO2_CAN_DISABLE_GC
+  const set<string> has_gc({"ndb-proto1", "ndb-proto2"});
+  if (cfg.disable_gc_ && !has_gc.count(db_type)) {
+    cerr << "[ERROR] benchmark " << db_type
+         << " does not have gc to disable" << endl;
+    return 1;
+  }
+#else
+  if (cfg.disable_gc_) {
+    cerr << "[ERROR] macro PROTO2_CAN_DISABLE_GC was not set, cannot disable gc" << endl;
+    return 1;
+  }
+#endif
+
+#ifdef PROTO2_CAN_DISABLE_SNAPSHOTS
+  const set<string> has_snapshots({"ndb-proto2"});
+  if (cfg.disable_snapshots_ && !has_snapshots.count(db_type)) {
+    cerr << "[ERROR] benchmark " << db_type
+         << " does not have snapshots to disable" << endl;
+    return 1;
+  }
+#else
+  if (cfg.disable_snapshots_) {
+    cerr << "[ERROR] macro PROTO2_CAN_DISABLE_SNAPSHOTS was not set, cannot disable snapshots" << endl;
+    return 1;
+  }
+#endif
+#ifdef CHECK_INVARIANTS
+  cerr << "WARNING: invariant checking is enabled - should disable for benchmark" << endl;
+#ifdef PARANOID_CHECKING
+  cerr << "  *** Paranoid checking is enabled ***" << endl;
+#endif
+#endif
+
+  if (verbose) {
+    const unsigned long ncpus = coreid::num_cpus_online();
+    cerr << "Database Benchmark:"                           << endl;
+    cerr << "  pid: " << getpid()                           << endl;
+    cerr << "settings:"                                     << endl;
+    cerr << "  par-loading : " << enable_parallel_loading   << endl;
+    cerr << "  pin-cpus    : " << pin_cpus                  << endl;
+    cerr << "  slow-exit   : " << slow_exit                 << endl;
+    cerr << "  retry-txns  : " << retry_aborted_transaction << endl;
+    cerr << "  backoff-txns: " << backoff_aborted_transaction << endl;
+    cerr << "  bench       : " << bench_type                << endl;
+    cerr << "  scale       : " << scale_factor              << endl;
+    cerr << "  num-cpus    : " << ncpus                     << endl;
+    cerr << "  num-threads : " << nthreads                  << endl;
+    cerr << "  db-type     : " << db_type                   << endl;
+    cerr << "  basedir     : " << basedir                   << endl;
+    cerr << "  txn-flags   : " << hexify(txn_flags)         << endl;
+    if (run_mode == RUNMODE_TIME)
+      cerr << "  runtime     : " << runtime                 << endl;
+    else
+      cerr << "  ops/worker  : " << ops_per_worker          << endl;
+#ifdef USE_VARINT_ENCODING
+    cerr << "  var-encode  : yes"                           << endl;
+#else
+    cerr << "  var-encode  : no"                            << endl;
+#endif
+
+#ifdef USE_JEMALLOC
+    cerr << "  allocator   : jemalloc"                      << endl;
+#elif defined USE_TCMALLOC
+    cerr << "  allocator   : tcmalloc"                      << endl;
+#elif defined USE_FLOW
+    cerr << "  allocator   : flow"                          << endl;
+#else
+    cerr << "  allocator   : libc"                          << endl;
+#endif
+    if (numa_memory > 0) {
+      cerr << "  numa-memory : " << numa_memory             << endl;
+    } else {
+      cerr << "  numa-memory : disabled"                    << endl;
+    }
+    cerr << "  logfiles : " << cfg.logfiles_                << endl;
+    cerr << "  assignments : " << cfg.assignments_          << endl;
+    cerr << "  disable-gc : " << cfg.disable_gc_            << endl;
+    cerr << "  disable-snapshots : " << cfg.disable_snapshots_ << endl;
+    cerr << "  stats-server-sockfile: " << stats_server_sockfile << endl;
+
+    cerr << "system properties:" << endl;
+    cerr << "  btree_internal_node_size: " << concurrent_btree::InternalNodeSize() << endl;
+    cerr << "  btree_leaf_node_size    : " << concurrent_btree::LeafNodeSize() << endl;
+
+#ifdef TUPLE_PREFETCH
+    cerr << "  tuple_prefetch          : yes" << endl;
+#else
+    cerr << "  tuple_prefetch          : no" << endl;
+#endif
+
+#ifdef BTREE_NODE_PREFETCH
+    cerr << "  btree_node_prefetch     : yes" << endl;
+#else
+    cerr << "  btree_node_prefetch     : no" << endl;
+#endif
+
+  }
+
+  if (!stats_server_sockfile.empty()) {
+    stats_server *srvr = new stats_server(stats_server_sockfile);
+    thread(&stats_server::serve_forever, srvr).detach();
+  }
+
+  vector<string> bench_toks = split_ws(bench_opts);
+  int argc = 1 + bench_toks.size();
+  char *argv[argc];
+  argv[0] = (char *) bench_type.c_str();
+  for (size_t i = 1; i <= bench_toks.size(); i++)
+    argv[i] = (char *) bench_toks[i - 1].c_str();
+  test_fn(db_type, cfg, argc, argv);
+  return 0;
+}
diff --git a/silo/new-benchmarks/kvdb_database.h b/silo/new-benchmarks/kvdb_database.h
new file mode 100644 (file)
index 0000000..dd70681
--- /dev/null
@@ -0,0 +1,800 @@
+#ifndef _KVDB_WRAPPER_IMPL_H_
+#define _KVDB_WRAPPER_IMPL_H_
+
+#include <vector>
+#include <limits>
+#include <utility>
+
+#include "../varint.h"
+#include "../macros.h"
+#include "../util.h"
+#include "../amd64.h"
+#include "../lockguard.h"
+#include "../prefetch.h"
+#include "../scopedperf.hh"
+#include "../counter.h"
+
+namespace private_ {
+  static event_avg_counter evt_avg_kvdb_stable_version_spins("avg_kvdb_stable_version_spins");
+  static event_avg_counter evt_avg_kvdb_lock_acquire_spins("avg_kvdb_lock_acquire_spins");
+  static event_avg_counter evt_avg_kvdb_read_retries("avg_kvdb_read_retries");
+
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_get_probe0, kvdb_get_probe0_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_get_probe1, kvdb_get_probe1_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_put_probe0, kvdb_put_probe0_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_insert_probe0, kvdb_insert_probe0_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_scan_probe0, kvdb_scan_probe0_cg);
+  STATIC_COUNTER_DECL(scopedperf::tsc_ctr, kvdb_remove_probe0, kvdb_remove_probe0_cg);
+}
+
+// defines single-threaded version
+template <bool UseConcurrencyControl>
+struct record_version {
+  uint16_t sz;
+
+  inline ALWAYS_INLINE bool
+  is_locked() const
+  {
+    return false;
+  }
+
+  inline ALWAYS_INLINE void lock() {}
+
+  inline ALWAYS_INLINE void unlock() {}
+
+  static inline ALWAYS_INLINE size_t
+  Size(uint32_t v)
+  {
+    return 0;
+  }
+
+  inline ALWAYS_INLINE size_t
+  size() const
+  {
+    return sz;
+  }
+
+  inline ALWAYS_INLINE void
+  set_size(size_t s)
+  {
+    INVARIANT(s <= std::numeric_limits<uint16_t>::max());
+    sz = s;
+  }
+
+  inline ALWAYS_INLINE uint32_t
+  stable_version() const
+  {
+    return 0;
+  }
+
+  inline ALWAYS_INLINE bool
+  check_version(uint32_t version) const
+  {
+    return true;
+  }
+};
+
+// concurrency control version
+template <>
+struct record_version<true> {
+  // [ locked | size  | version ]
+  // [  0..1  | 1..17 | 17..32  ]
+
+  static const uint32_t HDR_LOCKED_MASK = 0x1;
+
+  static const uint32_t HDR_SIZE_SHIFT = 1;
+  static const uint32_t HDR_SIZE_MASK = std::numeric_limits<uint16_t>::max() << HDR_SIZE_SHIFT;
+
+  static const uint32_t HDR_VERSION_SHIFT = 17;
+  static const uint32_t HDR_VERSION_MASK = ((uint32_t)-1) << HDR_VERSION_SHIFT;
+
+  record_version<true>() : hdr(0) {}
+
+  volatile uint32_t hdr;
+
+  static inline bool
+  IsLocked(uint32_t v)
+  {
+    return v & HDR_LOCKED_MASK;
+  }
+
+  inline bool
+  is_locked() const
+  {
+    return IsLocked(hdr);
+  }
+
+  inline void
+  lock()
+  {
+#ifdef ENABLE_EVENT_COUNTERS
+    unsigned long nspins = 0;
+#endif
+    uint32_t v = hdr;
+    while (IsLocked(v) ||
+           !__sync_bool_compare_and_swap(&hdr, v, v | HDR_LOCKED_MASK)) {
+      nop_pause();
+      v = hdr;
+#ifdef ENABLE_EVENT_COUNTERS
+      ++nspins;
+#endif
+    }
+    COMPILER_MEMORY_FENCE;
+#ifdef ENABLE_EVENT_COUNTERS
+    private_::evt_avg_kvdb_lock_acquire_spins.offer(nspins);
+#endif
+  }
+
+  inline void
+  unlock()
+  {
+    uint32_t v = hdr;
+    INVARIANT(IsLocked(v));
+    const uint32_t n = Version(v);
+    v &= ~HDR_VERSION_MASK;
+    v |= (((n + 1) << HDR_VERSION_SHIFT) & HDR_VERSION_MASK);
+    v &= ~HDR_LOCKED_MASK;
+    INVARIANT(!IsLocked(v));
+    COMPILER_MEMORY_FENCE;
+    hdr = v;
+  }
+
+  static inline size_t
+  Size(uint32_t v)
+  {
+    return (v & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT;
+  }
+
+  inline size_t
+  size() const
+  {
+    return Size(hdr);
+  }
+
+  inline void
+  set_size(size_t s)
+  {
+    INVARIANT(s <= std::numeric_limits<uint16_t>::max());
+    INVARIANT(is_locked());
+    const uint16_t new_sz = static_cast<uint16_t>(s);
+    hdr &= ~HDR_SIZE_MASK;
+    hdr |= (new_sz << HDR_SIZE_SHIFT);
+    INVARIANT(size() == s);
+  }
+
+  static inline uint32_t
+  Version(uint32_t v)
+  {
+    return (v & HDR_VERSION_MASK) >> HDR_VERSION_SHIFT;
+  }
+
+  inline uint32_t
+  stable_version() const
+  {
+    uint32_t v = hdr;
+#ifdef ENABLE_EVENT_COUNTERS
+    unsigned long nspins = 0;
+#endif
+    while (IsLocked(v)) {
+      nop_pause();
+      v = hdr;
+#ifdef ENABLE_EVENT_COUNTERS
+      ++nspins;
+#endif
+    }
+    COMPILER_MEMORY_FENCE;
+#ifdef ENABLE_EVENT_COUNTERS
+    private_::evt_avg_kvdb_stable_version_spins.offer(nspins);
+#endif
+    return v;
+  }
+
+  inline bool
+  check_version(uint32_t version) const
+  {
+    COMPILER_MEMORY_FENCE;
+    return hdr == version;
+  }
+};
+
+template <bool UseConcurrencyControl>
+struct basic_kvdb_record : public record_version<UseConcurrencyControl> {
+  typedef record_version<UseConcurrencyControl> super_type;
+  uint16_t alloc_size;
+  char data[0];
+
+  basic_kvdb_record(uint16_t alloc_size, const std::string &s)
+    : record_version<UseConcurrencyControl>(),
+      alloc_size(alloc_size)
+  {
+    NDB_MEMCPY(&data[0], s.data(), s.size());
+    this->set_size(s.size());
+  }
+
+  // just allocate, and set size to 0
+  basic_kvdb_record(uint16_t alloc_size)
+    : record_version<UseConcurrencyControl>(),
+      alloc_size(alloc_size)
+  {
+    this->set_size(0);
+  }
+
+  inline void
+  prefetch() const
+  {
+#ifdef TUPLE_PREFETCH
+    prefetch_bytes(this, sizeof(*this) + this->size());
+#endif
+  }
+
+private:
+  template <typename Reader, typename StringAllocator>
+  inline bool
+  do_guarded_read(Reader &reader, StringAllocator &sa) const
+  {
+    const size_t read_sz = this->size();
+    INVARIANT(read_sz);
+    return reader((uint8_t *) &this->data[0], read_sz, sa);
+  }
+
+public:
+
+  template <typename Reader, typename StringAllocator>
+  inline void
+  do_read(Reader &reader, StringAllocator &sa) const
+  {
+    if (UseConcurrencyControl) {
+#ifdef ENABLE_EVENT_COUNTERS
+      unsigned long nretries = 0;
+#endif
+    retry:
+      const uint32_t v = this->stable_version();
+      if (unlikely(!do_guarded_read(reader, sa) ||
+                   !this->check_version(v))) {
+#ifdef ENABLE_EVENT_COUNTERS
+        ++nretries;
+#endif
+        goto retry;
+      }
+#ifdef ENABLE_EVENT_COUNTERS
+      private_::evt_avg_kvdb_read_retries.offer(nretries);
+#endif
+    } else {
+      const bool ret = do_guarded_read(reader, sa);
+      if (!ret)
+        INVARIANT(false);
+    }
+  }
+
+  template <typename Writer>
+  inline bool
+  do_write(Writer &writer)
+  {
+    INVARIANT(!UseConcurrencyControl || this->is_locked());
+    const size_t new_sz = writer.compute_needed((const uint8_t *) &this->data[0], this->size());
+    if (unlikely(new_sz > alloc_size))
+      return false;
+    writer((uint8_t *) &this->data[0], this->size());
+    this->set_size(new_sz);
+    return true;
+  }
+
+  static basic_kvdb_record *
+  alloc(size_t alloc_sz)
+  {
+    INVARIANT(alloc_sz <= std::numeric_limits<uint16_t>::max());
+    const size_t max_alloc_sz =
+      std::numeric_limits<uint16_t>::max() + sizeof(basic_kvdb_record);
+    const size_t actual_alloc_sz =
+      std::min(
+          util::round_up<size_t, allocator::LgAllocAlignment>(sizeof(basic_kvdb_record) + alloc_sz),
+          max_alloc_sz);
+    char * const p = reinterpret_cast<char *>(rcu::s_instance.alloc(actual_alloc_sz));
+    INVARIANT(p);
+    return new (p) basic_kvdb_record(actual_alloc_sz - sizeof(basic_kvdb_record));
+  }
+
+  static basic_kvdb_record *
+  alloc(const std::string &s)
+  {
+    const size_t sz = s.size();
+    const size_t max_alloc_sz =
+      std::numeric_limits<uint16_t>::max() + sizeof(basic_kvdb_record);
+    const size_t alloc_sz =
+      std::min(
+          util::round_up<size_t, allocator::LgAllocAlignment>(sizeof(basic_kvdb_record) + sz),
+          max_alloc_sz);
+    char * const p = reinterpret_cast<char *>(rcu::s_instance.alloc(alloc_sz));
+    INVARIANT(p);
+    return new (p) basic_kvdb_record(alloc_sz - sizeof(basic_kvdb_record), s);
+  }
+
+private:
+  static inline void
+  deleter(void *r)
+  {
+    basic_kvdb_record * const px =
+      reinterpret_cast<basic_kvdb_record *>(r);
+    const size_t alloc_sz = px->alloc_size + sizeof(*px);
+    px->~basic_kvdb_record();
+    rcu::s_instance.dealloc(px, alloc_sz);
+  }
+
+public:
+  static void
+  release(basic_kvdb_record *r)
+  {
+    if (unlikely(!r))
+      return;
+    rcu::s_instance.free_with_fn(r, deleter);
+  }
+
+  static void
+  release_no_rcu(basic_kvdb_record *r)
+  {
+    if (unlikely(!r))
+      return;
+    deleter(r);
+  }
+
+} PACKED;
+
+template <typename Btree, bool UseConcurrencyControl>
+struct purge_tree_walker : public Btree::tree_walk_callback {
+  typedef basic_kvdb_record<UseConcurrencyControl> kvdb_record;
+
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+  purge_tree_walker()
+    : purge_stats_nodes(0),
+      purge_stats_nosuffix_nodes(0) {}
+  std::vector<uint16_t> purge_stats_nkeys_node;
+  size_t purge_stats_nodes;
+  size_t purge_stats_nosuffix_nodes;
+
+  void
+  dump_stats()
+  {
+    size_t v = 0;
+    for (std::vector<uint16_t>::iterator it = purge_stats_nkeys_node.begin();
+        it != purge_stats_nkeys_node.end(); ++it)
+      v += *it;
+    const double avg_nkeys_node = double(v)/double(purge_stats_nkeys_node.size());
+    const double avg_fill_factor = avg_nkeys_node/double(Btree::NKeysPerNode);
+    std::cerr << "btree node stats" << std::endl;
+    std::cerr << "    avg_nkeys_node: " << avg_nkeys_node << std::endl;
+    std::cerr << "    avg_fill_factor: " << avg_fill_factor << std::endl;
+    std::cerr << "    num_nodes: " << purge_stats_nodes << std::endl;
+    std::cerr << "    num_nosuffix_nodes: " << purge_stats_nosuffix_nodes << std::endl;
+  }
+#endif
+
+  virtual void
+  on_node_begin(const typename Btree::node_opaque_t *n)
+  {
+    INVARIANT(spec_values.empty());
+    spec_values = Btree::ExtractValues(n);
+  }
+
+  virtual void
+  on_node_success()
+  {
+    for (size_t i = 0; i < spec_values.size(); i++) {
+      kvdb_record * const r = (kvdb_record *) spec_values[i].first;
+      kvdb_record::release_no_rcu(r);
+    }
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+    purge_stats_nkeys_node.push_back(spec_values.size());
+    purge_stats_nodes++;
+    for (size_t i = 0; i < spec_values.size(); i++)
+      if (spec_values[i].second)
+        goto done;
+    purge_stats_nosuffix_nodes++;
+done:
+#endif
+    spec_values.clear();
+  }
+
+  virtual void
+  on_node_failure()
+  {
+    spec_values.clear();
+  }
+
+private:
+  std::vector<std::pair<typename Btree::value_type, bool>> spec_values;
+};
+
+class kvdb_txn {
+public:
+  inline kvdb_txn(uint64_t, str_arena &a) : a(&a) {}
+  inline str_arena & string_allocator() { return *a; }
+
+  inline bool
+  commit()
+  {
+    return true;
+  }
+
+  inline void
+  abort()
+  {
+    // should never abort
+    ALWAYS_ASSERT(false);
+  }
+
+private:
+  str_arena *a;
+  scoped_rcu_region region;
+};
+
+template <typename Schema, bool UseConcurrencyControl>
+class kvdb_index : public abstract_ordered_index {
+public:
+
+  typedef typename Schema::base_type base_type;
+  typedef typename Schema::key_type key_type;
+  typedef typename Schema::value_type value_type;
+  typedef typename Schema::value_descriptor_type value_descriptor_type;
+  typedef typename Schema::key_encoder_type key_encoder_type;
+  typedef typename Schema::value_encoder_type value_encoder_type;
+
+  static const uint64_t AllFieldsMask = typed_txn_btree_<Schema>::AllFieldsMask;
+  typedef util::Fields<AllFieldsMask> AllFields;
+
+  struct search_range_callback {
+  public:
+    virtual ~search_range_callback() {}
+    virtual bool invoke(const key_type &k, const value_type &v) = 0;
+  };
+
+  struct bytes_search_range_callback {
+  public:
+    virtual ~bytes_search_range_callback() {}
+    virtual bool invoke(const std::string &k, const std::string &v) = 0;
+  };
+
+private:
+  // leverage the definitions for txn_btree and typed_txn_btree
+
+  typedef txn_btree_::key_reader bytes_key_reader;
+  typedef txn_btree_::single_value_reader bytes_single_value_reader;
+  typedef txn_btree_::value_reader bytes_value_reader;
+
+  typedef
+    typename typed_txn_btree_<Schema>::key_writer
+    key_writer;
+  typedef
+    typename typed_txn_btree_<Schema>::key_reader
+    key_reader;
+
+  typedef
+    typename typed_txn_btree_<Schema>::value_writer
+    value_writer;
+  typedef
+    typename typed_txn_btree_<Schema>::single_value_reader
+    single_value_reader;
+  typedef
+    typename typed_txn_btree_<Schema>::value_reader
+    value_reader;
+
+  typedef basic_kvdb_record<UseConcurrencyControl> kvdb_record;
+
+  template <typename Btree, typename Callback, typename KeyReader, typename ValueReader>
+  class kvdb_wrapper_search_range_callback : public Btree::search_range_callback {
+  public:
+
+    kvdb_wrapper_search_range_callback(
+        Callback &upcall,
+        KeyReader &kr,
+        ValueReader &vr,
+        str_arena &arena)
+      : upcall(&upcall), kr(&kr),
+        vr(&vr), arena(&arena) {}
+
+    virtual bool
+    invoke(const typename Btree::string_type &k, typename Btree::value_type v)
+    {
+      const kvdb_record * const r =
+        reinterpret_cast<const kvdb_record *>(v);
+      r->prefetch();
+      r->do_read(*vr, *arena);
+      return upcall->invoke((*kr)(k), vr->results());
+    }
+
+  private:
+    Callback *upcall;
+    KeyReader *kr;
+    ValueReader *vr;
+    str_arena *arena;
+  };
+
+public:
+
+  kvdb_index(size_t value_size_hint,
+            bool mostly_append,
+            const std::string &name)
+    : name(name)
+  {}
+
+  // virtual interface
+
+  virtual size_t
+  size() const OVERRIDE
+  {
+    return btr.size();
+  }
+
+  virtual std::map<std::string, uint64_t>
+  clear() OVERRIDE
+  {
+    purge_tree_walker<my_btree, UseConcurrencyControl> w;
+    btr.tree_walk(w);
+    btr.clear();
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+    std::cerr << "purging kvdb index: " << name << std::endl;
+    w.dump_stats();
+#endif
+    return std::map<std::string, uint64_t>();
+  }
+
+  // templated interface
+
+  template <typename FieldsMask = AllFields>
+  inline bool search(
+      kvdb_txn &t, const key_type &k, value_type &v,
+      FieldsMask fm = FieldsMask());
+
+  template <typename FieldsMask = AllFields>
+  inline void search_range_call(
+      kvdb_txn &t, const key_type &lower, const key_type *upper,
+      search_range_callback &callback,
+      bool no_key_results = false /* skip decoding of keys? */,
+      FieldsMask fm = FieldsMask());
+
+  // a lower-level variant which does not bother to decode the key/values
+  inline void bytes_search_range_call(
+      kvdb_txn &t, const key_type &lower, const key_type *upper,
+      bytes_search_range_callback &callback,
+      size_t value_fields_prefix = std::numeric_limits<size_t>::max());
+
+  template <typename FieldsMask = AllFields>
+  inline void put(
+      kvdb_txn &t, const key_type &k, const value_type &v,
+      FieldsMask fm = FieldsMask());
+
+  inline void insert(
+      kvdb_txn &t, const key_type &k, const value_type &v);
+
+  inline void remove(
+      kvdb_txn &t, const key_type &k);
+
+private:
+
+  template <typename Callback, typename KeyReader, typename ValueReader>
+  inline void do_search_range_call(
+      kvdb_txn &t, const key_type &lower, const key_type *upper,
+      Callback &callback, KeyReader &kr, ValueReader &vr);
+
+  std::string name;
+  typedef
+    typename std::conditional<
+      UseConcurrencyControl,
+      concurrent_btree,
+      single_threaded_btree>::type
+    my_btree;
+   my_btree btr;
+};
+
+template <bool UseConcurrencyControl>
+class kvdb_database : public abstract_db {
+public:
+
+  template <typename Schema>
+  struct IndexType {
+    typedef kvdb_index<Schema, UseConcurrencyControl> type;
+    typedef std::shared_ptr<type> ptr_type;
+  };
+
+  template <enum abstract_db::TxnProfileHint hint>
+  struct TransactionType
+  {
+    typedef kvdb_txn type;
+    typedef std::shared_ptr<type> ptr_type;
+  };
+
+  template <enum abstract_db::TxnProfileHint hint>
+  inline typename TransactionType<hint>::ptr_type
+  new_txn(uint64_t txn_flags, str_arena &arena) const
+  {
+    return std::make_shared<typename TransactionType<hint>::type>(txn_flags, arena);
+  }
+
+  typedef transaction_abort_exception abort_exception_type;
+
+  virtual void
+  do_txn_epoch_sync() const OVERRIDE
+  {
+  }
+
+  virtual void
+  do_txn_finish() const OVERRIDE
+  {
+  }
+
+  template <typename Schema>
+  inline typename IndexType<Schema>::ptr_type
+  open_index(const std::string &name,
+             size_t value_size_hint,
+             bool mostly_append)
+  {
+    return std::make_shared<typename IndexType<Schema>::type>(
+        value_size_hint, mostly_append, name);
+  }
+};
+
+template <typename Schema, bool UseConcurrencyControl>
+template <typename FieldsMask>
+bool
+kvdb_index<Schema, UseConcurrencyControl>::search(
+    kvdb_txn &t, const key_type &k, value_type &v,
+    FieldsMask fm)
+{
+  key_writer kw(&k);
+  const std::string * const keypx =
+    kw.fully_materialize(false, t.string_allocator());
+
+  typedef basic_kvdb_record<UseConcurrencyControl> kvdb_record;
+  ANON_REGION("kvdb_ordered_index::get:", &private_::kvdb_get_probe0_cg);
+  typename my_btree::value_type p = 0;
+  if (btr.search(varkey(*keypx), p)) {
+    ANON_REGION("kvdb_ordered_index::get:do_read:", &private_::kvdb_get_probe1_cg);
+    const kvdb_record * const r = reinterpret_cast<const kvdb_record *>(p);
+    r->prefetch();
+    single_value_reader vr(v, FieldsMask::value);
+    r->do_read(vr, t.string_allocator());
+    return true;
+  }
+  return false;
+}
+
+template <typename Schema, bool UseConcurrencyControl>
+template <typename FieldsMask>
+void
+kvdb_index<Schema, UseConcurrencyControl>::search_range_call(
+    kvdb_txn &t, const key_type &lower, const key_type *upper,
+    search_range_callback &callback,
+    bool no_key_results,
+    FieldsMask fm)
+{
+  key_reader kr(no_key_results);
+  value_reader vr(FieldsMask::value);
+
+  do_search_range_call(t, lower, upper, callback, kr, vr);
+}
+
+template <typename Schema, bool UseConcurrencyControl>
+void
+kvdb_index<Schema, UseConcurrencyControl>::bytes_search_range_call(
+    kvdb_txn &t, const key_type &lower, const key_type *upper,
+    bytes_search_range_callback &callback,
+    size_t value_fields_prefix)
+{
+  const value_encoder_type value_encoder;
+  const size_t max_bytes_read =
+    value_encoder.encode_max_nbytes_prefix(value_fields_prefix);
+  bytes_key_reader kr;
+  bytes_value_reader vr(max_bytes_read);
+
+  do_search_range_call(t, lower, upper, callback, kr, vr);
+}
+
+template <typename Schema, bool UseConcurrencyControl>
+template <typename FieldsMask>
+void
+kvdb_index<Schema, UseConcurrencyControl>::put(
+    kvdb_txn &t, const key_type &key, const value_type &value,
+    FieldsMask fm)
+{
+  key_writer kw(&key);
+  const std::string * const keypx =
+    kw.fully_materialize(false, t.string_allocator());
+  if (UseConcurrencyControl)
+    // XXX: currently unsupported- need to ensure locked values
+    // are the canonical versions pointed to by the tree
+    ALWAYS_ASSERT(false);
+  value_writer vw(&value, FieldsMask::value);
+  typename my_btree::value_type v = 0, v_old = 0;
+  if (btr.search(varkey(*keypx), v)) {
+    kvdb_record * const r = reinterpret_cast<kvdb_record *>(v);
+    r->prefetch();
+    lock_guard<kvdb_record> guard(*r);
+    if (r->do_write(vw))
+      return;
+    // replace - slow-path
+    kvdb_record * const rnew =
+      kvdb_record::alloc(*vw.fully_materialize(false, t.string_allocator()));
+    btr.insert(varkey(*keypx), (typename my_btree::value_type) rnew, &v_old, 0);
+    INVARIANT((typename my_btree::value_type) r == v_old);
+    // rcu-free the old record
+    kvdb_record::release(r);
+    return;
+  }
+
+  // also slow-path
+  kvdb_record * const rnew =
+    kvdb_record::alloc(*vw.fully_materialize(false, t.string_allocator()));
+  if (!btr.insert(varkey(*keypx), (typename my_btree::value_type) rnew, &v_old, 0)) {
+    kvdb_record * const r = (kvdb_record *) v_old;
+    kvdb_record::release(r);
+  }
+  return;
+}
+
+template <typename Schema, bool UseConcurrencyControl>
+void
+kvdb_index<Schema, UseConcurrencyControl>::insert(
+    kvdb_txn &t, const key_type &k, const value_type &v)
+{
+  key_writer kw(&k);
+  const std::string * const keypx =
+    kw.fully_materialize(false, t.string_allocator());
+  if (UseConcurrencyControl)
+    // XXX: currently unsupported- see above
+    ALWAYS_ASSERT(false);
+  value_writer vw(&v, AllFieldsMask);
+  const size_t sz = vw.compute_needed(nullptr, 0);
+  kvdb_record * const rec = kvdb_record::alloc(sz);
+  vw((uint8_t *) &rec->data[0], 0);
+  rec->set_size(sz);
+  if (likely(btr.insert_if_absent(varkey(*keypx), (typename my_btree::value_type) rec, nullptr)))
+    return;
+  kvdb_record::release_no_rcu(rec);
+  put(t, k, v);
+}
+
+template <typename Schema, bool UseConcurrencyControl>
+void
+kvdb_index<Schema, UseConcurrencyControl>::remove(
+    kvdb_txn &t, const key_type &k)
+{
+  key_writer kw(&k);
+  const std::string * const keypx =
+    kw.fully_materialize(false, t.string_allocator());
+  ANON_REGION("kvdb_ordered_index::remove:", &private_::kvdb_remove_probe0_cg);
+  if (UseConcurrencyControl)
+    // XXX: currently unsupported- see above
+    ALWAYS_ASSERT(false);
+  typename my_btree::value_type v = 0;
+  if (likely(btr.remove(varkey(*keypx), &v))) {
+    kvdb_record * const r = reinterpret_cast<kvdb_record *>(v);
+    kvdb_record::release(r);
+  }
+}
+
+template <typename Schema, bool UseConcurrencyControl>
+template <typename Callback, typename KeyReader, typename ValueReader>
+void
+kvdb_index<Schema, UseConcurrencyControl>::do_search_range_call(
+      kvdb_txn &t, const key_type &lower, const key_type *upper,
+      Callback &callback, KeyReader &kr, ValueReader &vr)
+{
+  key_writer lower_key_writer(&lower);
+  key_writer upper_key_writer(upper);
+  const std::string * const lower_str =
+    lower_key_writer.fully_materialize(false, t.string_allocator());
+  const std::string * const upper_str =
+    upper_key_writer.fully_materialize(false, t.string_allocator());
+
+  kvdb_wrapper_search_range_callback<
+    my_btree,
+    Callback,
+    KeyReader,
+    ValueReader> c(callback, kr, vr, t.string_allocator());
+
+  varkey uppervk;
+  if (upper_str)
+    uppervk = varkey(*upper_str);
+  btr.search_range_call(varkey(*lower_str), upper_str ? &uppervk : nullptr, c, t.string_allocator()());
+}
+
+#endif /* _KVDB_WRAPPER_IMPL_H_ */
diff --git a/silo/new-benchmarks/ndb_database.h b/silo/new-benchmarks/ndb_database.h
new file mode 100644 (file)
index 0000000..af91ebc
--- /dev/null
@@ -0,0 +1,237 @@
+#pragma once
+
+#include <memory>
+
+#include "abstract_db.h"
+#include "../typed_txn_btree.h"
+#include "../txn.h"
+#include "../txn_impl.h"
+#include "../txn_proto2_impl.h"
+
+template <template <typename> class Transaction, typename Schema>
+class ndb_index : public abstract_ordered_index,
+                  public typed_txn_btree<Transaction, Schema> {
+  typedef typed_txn_btree<Transaction, Schema> super_type;
+public:
+
+  ndb_index(size_t value_size_hint,
+            bool mostly_append,
+            const std::string &name)
+    : super_type(value_size_hint, mostly_append, name),
+      name(name)
+  {}
+
+  virtual size_t
+  size() const OVERRIDE
+  {
+    return this->size_estimate();
+  }
+
+  virtual std::map<std::string, uint64_t>
+  clear() OVERRIDE
+  {
+#ifdef TXN_BTREE_DUMP_PURGE_STATS
+    std::cerr << "purging txn index: " << name << std::endl;
+#endif
+    return this->unsafe_purge(true);
+  }
+
+private:
+  std::string name;
+};
+
+namespace private_ {
+
+  template <enum abstract_db::TxnProfileHint> struct ndb_txn_type {};
+
+  struct default_traits : public default_transaction_traits {
+    typedef str_arena StringAllocator;
+  };
+
+  // ycsb profiles
+
+  struct hint_kv_get_put_traits {
+    static const size_t read_set_expected_size = 1;
+    static const size_t write_set_expected_size = 1;
+    static const size_t absent_set_expected_size = 1;
+    static const bool stable_input_memory = true;
+    static const bool hard_expected_sizes = true;
+    static const bool read_own_writes = false;
+    typedef str_arena StringAllocator;
+  };
+
+  struct hint_kv_rmw_traits : public hint_kv_get_put_traits {};
+
+  struct hint_kv_scan_traits {
+    static const size_t read_set_expected_size = 100;
+    static const size_t write_set_expected_size = 1;
+    static const size_t absent_set_expected_size = read_set_expected_size / 7 + 1;
+    static const bool stable_input_memory = true;
+    static const bool hard_expected_sizes = false;
+    static const bool read_own_writes = false;
+    typedef str_arena StringAllocator;
+  };
+
+  // tpcc profiles
+
+  struct hint_read_only_traits {
+    static const size_t read_set_expected_size = 1;
+    static const size_t write_set_expected_size = 1;
+    static const size_t absent_set_expected_size = 1;
+    static const bool stable_input_memory = true;
+    static const bool hard_expected_sizes = true;
+    static const bool read_own_writes = false;
+    typedef str_arena StringAllocator;
+  };
+
+  struct hint_tpcc_new_order_traits {
+    static const size_t read_set_expected_size = 35;
+    static const size_t write_set_expected_size = 35;
+    static const size_t absent_set_expected_size = 1;
+    static const bool stable_input_memory = true;
+    static const bool hard_expected_sizes = true;
+    static const bool read_own_writes = false;
+    typedef str_arena StringAllocator;
+  };
+
+  struct hint_tpcc_payment_traits {
+    static const size_t read_set_expected_size = 85;
+    static const size_t write_set_expected_size = 10;
+    static const size_t absent_set_expected_size = 15;
+    static const bool stable_input_memory = true;
+    static const bool hard_expected_sizes = false;
+    static const bool read_own_writes = false;
+    typedef str_arena StringAllocator;
+  };
+
+  struct hint_tpcc_delivery_traits {
+    static const size_t read_set_expected_size = 175;
+    static const size_t write_set_expected_size = 175;
+    static const size_t absent_set_expected_size = 35;
+    static const bool stable_input_memory = true;
+    static const bool hard_expected_sizes = false;
+    static const bool read_own_writes = false;
+    typedef str_arena StringAllocator;
+  };
+
+  struct hint_tpcc_order_status_traits {
+    static const size_t read_set_expected_size = 95;
+    static const size_t write_set_expected_size = 1;
+    static const size_t absent_set_expected_size = 25;
+    static const bool stable_input_memory = true;
+    static const bool hard_expected_sizes = false;
+    static const bool read_own_writes = false;
+    typedef str_arena StringAllocator;
+  };
+
+  struct hint_tpcc_order_status_read_only_traits : public hint_read_only_traits {};
+
+  struct hint_tpcc_stock_level_traits {
+    static const size_t read_set_expected_size = 500;
+    static const size_t write_set_expected_size = 1;
+    static const size_t absent_set_expected_size = 25;
+    static const bool stable_input_memory = true;
+    static const bool hard_expected_sizes = false;
+    static const bool read_own_writes = false;
+    typedef str_arena StringAllocator;
+  };
+
+  struct hint_tpcc_stock_level_read_only_traits : public hint_read_only_traits {};
+
+#define TXN_PROFILE_HINT_OP(x) \
+  x(abstract_db::HINT_DEFAULT, default_traits) \
+  x(abstract_db::HINT_KV_GET_PUT, hint_kv_get_put_traits) \
+  x(abstract_db::HINT_KV_RMW, hint_kv_rmw_traits) \
+  x(abstract_db::HINT_KV_SCAN, hint_kv_scan_traits) \
+  x(abstract_db::HINT_TPCC_NEW_ORDER, hint_tpcc_new_order_traits) \
+  x(abstract_db::HINT_TPCC_PAYMENT, hint_tpcc_payment_traits) \
+  x(abstract_db::HINT_TPCC_DELIVERY, hint_tpcc_delivery_traits) \
+  x(abstract_db::HINT_TPCC_ORDER_STATUS, hint_tpcc_order_status_traits) \
+  x(abstract_db::HINT_TPCC_ORDER_STATUS_READ_ONLY, hint_tpcc_order_status_read_only_traits) \
+  x(abstract_db::HINT_TPCC_STOCK_LEVEL, hint_tpcc_stock_level_traits) \
+  x(abstract_db::HINT_TPCC_STOCK_LEVEL_READ_ONLY, hint_tpcc_stock_level_read_only_traits)
+
+#define SPECIALIZE_OP_HINTS_X(hint, traitstype) \
+  template <> struct ndb_txn_type< hint > { \
+    typedef traitstype type; \
+  };
+
+TXN_PROFILE_HINT_OP(SPECIALIZE_OP_HINTS_X)
+
+#undef SPECIALIZE_OP_HINTS_X
+}
+
+template <template <typename> class Transaction>
+class ndb_database : public abstract_db {
+public:
+
+  template <typename Schema>
+  struct IndexType {
+    typedef ndb_index<Transaction, Schema> type;
+    typedef std::shared_ptr<type> ptr_type;
+  };
+
+  template <enum abstract_db::TxnProfileHint hint>
+  struct TransactionType
+  {
+    typedef Transaction<typename private_::ndb_txn_type<hint>::type> type;
+    typedef std::shared_ptr<type> ptr_type;
+  };
+
+  template <enum abstract_db::TxnProfileHint hint>
+  inline typename TransactionType<hint>::ptr_type
+  new_txn(uint64_t txn_flags, str_arena &arena) const
+  {
+    return std::make_shared<typename TransactionType<hint>::type>(txn_flags, arena);
+  }
+
+  typedef transaction_abort_exception abort_exception_type;
+
+  ssize_t txn_max_batch_size() const OVERRIDE { return 100; }
+
+  void
+  do_txn_epoch_sync() const OVERRIDE
+  {
+    txn_epoch_sync<Transaction>::sync();
+  }
+
+  void
+  do_txn_finish() const OVERRIDE
+  {
+    txn_epoch_sync<Transaction>::finish();
+  }
+
+  void
+  thread_init(bool loader) OVERRIDE
+  {
+    txn_epoch_sync<Transaction>::thread_init(loader);
+  }
+
+  void
+  thread_end() OVERRIDE
+  {
+    txn_epoch_sync<Transaction>::thread_end();
+  }
+
+  std::tuple<uint64_t, uint64_t, double>
+  get_ntxn_persisted() const OVERRIDE
+  {
+    return txn_epoch_sync<Transaction>::compute_ntxn_persisted();
+  }
+
+  void
+  reset_ntxn_persisted() OVERRIDE
+  {
+    txn_epoch_sync<Transaction>::reset_ntxn_persisted();
+  }
+
+  template <typename Schema>
+  inline typename IndexType<Schema>::ptr_type
+  open_index(const std::string &name,
+             size_t value_size_hint,
+             bool mostly_append)
+  {
+    return std::make_shared<typename IndexType<Schema>::type>(
+        value_size_hint, mostly_append, name);
+  }
+};
diff --git a/silo/new-benchmarks/tpcc.cc b/silo/new-benchmarks/tpcc.cc
new file mode 100644 (file)
index 0000000..bb6a77b
--- /dev/null
@@ -0,0 +1,2276 @@
+/**
+ * An implementation of TPC-C based off of:
+ * https://github.com/oltpbenchmark/oltpbench/tree/master/src/com/oltpbenchmark/benchmarks/tpcc
+ */
+
+#include <sys/time.h>
+#include <string>
+#include <ctype.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <set>
+#include <vector>
+
+#include "../txn.h"
+#include "../macros.h"
+#include "../scopedperf.hh"
+#include "../spinlock.h"
+
+#include "bench.h"
+#include "tpcc.h"
+
+#include "ndb_database.h"
+#include "kvdb_database.h"
+
+using namespace std;
+using namespace util;
+
+static inline ALWAYS_INLINE size_t
+NumWarehouses()
+{
+  return (size_t) scale_factor;
+}
+
+// config constants
+
+static constexpr inline ALWAYS_INLINE size_t
+NumItems()
+{
+  return 100000;
+}
+
+static constexpr inline ALWAYS_INLINE size_t
+NumDistrictsPerWarehouse()
+{
+  return 10;
+}
+
+static constexpr inline ALWAYS_INLINE size_t
+NumCustomersPerDistrict()
+{
+  return 3000;
+}
+
+// T must implement lock()/unlock(). Both must *not* throw exceptions
+template <typename T>
+class scoped_multilock {
+public:
+  inline scoped_multilock()
+    : did_lock(false)
+  {
+  }
+
+  inline ~scoped_multilock()
+  {
+    if (did_lock)
+      for (auto &t : locks)
+        t->unlock();
+  }
+
+  inline void
+  enq(T &t)
+  {
+    ALWAYS_ASSERT(!did_lock);
+    locks.emplace_back(&t);
+  }
+
+  inline void
+  multilock()
+  {
+    ALWAYS_ASSERT(!did_lock);
+    if (locks.size() > 1)
+      sort(locks.begin(), locks.end());
+#ifdef CHECK_INVARIANTS
+    if (set<T *>(locks.begin(), locks.end()).size() != locks.size()) {
+      for (auto &t : locks)
+        cerr << "lock: " << hexify(t) << endl;
+      INVARIANT(false && "duplicate locks found");
+    }
+#endif
+    for (auto &t : locks)
+      t->lock();
+    did_lock = true;
+  }
+
+private:
+  bool did_lock;
+  typename util::vec<T *, 64>::type locks;
+};
+
+// like a lock_guard, but has the option of not acquiring
+template <typename T>
+class scoped_lock_guard {
+public:
+  inline scoped_lock_guard(T &l)
+    : l(&l)
+  {
+    this->l->lock();
+  }
+
+  inline scoped_lock_guard(T *l)
+    : l(l)
+  {
+    if (this->l)
+      this->l->lock();
+  }
+
+  inline ~scoped_lock_guard()
+  {
+    if (l)
+      l->unlock();
+  }
+
+private:
+  T *l;
+};
+
+// configuration flags
+static int g_disable_xpartition_txn = 0;
+static int g_disable_read_only_scans = 0;
+static int g_enable_partition_locks = 0;
+static int g_enable_separate_tree_per_partition = 0;
+static int g_new_order_remote_item_pct = 1;
+static int g_new_order_fast_id_gen = 0;
+static int g_uniform_item_dist = 0;
+static unsigned g_txn_workload_mix[] = { 45, 43, 4, 4, 4 }; // default TPC-C workload mix
+
+static aligned_padded_elem<spinlock> *g_partition_locks = nullptr;
+static aligned_padded_elem<atomic<uint64_t>> *g_district_ids = nullptr;
+
+// maps a wid => partition id
+static inline ALWAYS_INLINE unsigned int
+PartitionId(unsigned int wid)
+{
+  INVARIANT(wid >= 1 && wid <= NumWarehouses());
+  wid -= 1; // 0-idx
+  if (NumWarehouses() <= nthreads)
+    // more workers than partitions, so its easy
+    return wid;
+  const unsigned nwhse_per_partition = NumWarehouses() / nthreads;
+  const unsigned partid = wid / nwhse_per_partition;
+  if (partid >= nthreads)
+    return nthreads - 1;
+  return partid;
+}
+
+static inline ALWAYS_INLINE spinlock &
+LockForPartition(unsigned int wid)
+{
+  INVARIANT(g_enable_partition_locks);
+  return g_partition_locks[PartitionId(wid)].elem;
+}
+
+static inline atomic<uint64_t> &
+NewOrderIdHolder(unsigned warehouse, unsigned district)
+{
+  INVARIANT(warehouse >= 1 && warehouse <= NumWarehouses());
+  INVARIANT(district >= 1 && district <= NumDistrictsPerWarehouse());
+  const unsigned idx =
+    (warehouse - 1) * NumDistrictsPerWarehouse() + (district - 1);
+  return g_district_ids[idx].elem;
+}
+
+static inline uint64_t
+FastNewOrderIdGen(unsigned warehouse, unsigned district)
+{
+  return NewOrderIdHolder(warehouse, district).fetch_add(1, memory_order_acq_rel);
+}
+
+struct checker {
+  // these sanity checks are just a few simple checks to make sure
+  // the data is not entirely corrupted
+
+  static inline ALWAYS_INLINE void
+  SanityCheckCustomer(const customer::key *k, const customer::value *v)
+  {
+    INVARIANT(k->c_w_id >= 1 && static_cast<size_t>(k->c_w_id) <= NumWarehouses());
+    INVARIANT(k->c_d_id >= 1 && static_cast<size_t>(k->c_d_id) <= NumDistrictsPerWarehouse());
+    INVARIANT(k->c_id >= 1 && static_cast<size_t>(k->c_id) <= NumCustomersPerDistrict());
+#ifdef DISABLE_FIELD_SELECTION
+    INVARIANT(v->c_credit == "BC" || v->c_credit == "GC");
+    INVARIANT(v->c_middle == "OE");
+#endif
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckWarehouse(const warehouse::key *k, const warehouse::value *v)
+  {
+    INVARIANT(k->w_id >= 1 && static_cast<size_t>(k->w_id) <= NumWarehouses());
+#ifdef DISABLE_FIELD_SELECTION
+    INVARIANT(v->w_state.size() == 2);
+    INVARIANT(v->w_zip == "123456789");
+#endif
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckDistrict(const district::key *k, const district::value *v)
+  {
+    INVARIANT(k->d_w_id >= 1 && static_cast<size_t>(k->d_w_id) <= NumWarehouses());
+    INVARIANT(k->d_id >= 1 && static_cast<size_t>(k->d_id) <= NumDistrictsPerWarehouse());
+#ifdef DISABLE_FIELD_SELECTION
+    INVARIANT(v->d_next_o_id >= 3001);
+    INVARIANT(v->d_state.size() == 2);
+    INVARIANT(v->d_zip == "123456789");
+#endif
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckItem(const item::key *k, const item::value *v)
+  {
+    INVARIANT(k->i_id >= 1 && static_cast<size_t>(k->i_id) <= NumItems());
+#ifdef DISABLE_FIELD_SELECTION
+    INVARIANT(v->i_price >= 1.0 && v->i_price <= 100.0);
+#endif
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckStock(const stock::key *k, const stock::value *v)
+  {
+    INVARIANT(k->s_w_id >= 1 && static_cast<size_t>(k->s_w_id) <= NumWarehouses());
+    INVARIANT(k->s_i_id >= 1 && static_cast<size_t>(k->s_i_id) <= NumItems());
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckNewOrder(const new_order::key *k, const new_order::value *v)
+  {
+    INVARIANT(k->no_w_id >= 1 && static_cast<size_t>(k->no_w_id) <= NumWarehouses());
+    INVARIANT(k->no_d_id >= 1 && static_cast<size_t>(k->no_d_id) <= NumDistrictsPerWarehouse());
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckOOrder(const oorder::key *k, const oorder::value *v)
+  {
+    INVARIANT(k->o_w_id >= 1 && static_cast<size_t>(k->o_w_id) <= NumWarehouses());
+    INVARIANT(k->o_d_id >= 1 && static_cast<size_t>(k->o_d_id) <= NumDistrictsPerWarehouse());
+#ifdef DISABLE_FIELD_SELECTION
+    INVARIANT(v->o_c_id >= 1 && static_cast<size_t>(v->o_c_id) <= NumCustomersPerDistrict());
+    INVARIANT(v->o_carrier_id >= 0 && static_cast<size_t>(v->o_carrier_id) <= NumDistrictsPerWarehouse());
+    INVARIANT(v->o_ol_cnt >= 5 && v->o_ol_cnt <= 15);
+#endif
+  }
+
+  static inline ALWAYS_INLINE void
+  SanityCheckOrderLine(const order_line::key *k, const order_line::value *v)
+  {
+    INVARIANT(k->ol_w_id >= 1 && static_cast<size_t>(k->ol_w_id) <= NumWarehouses());
+    INVARIANT(k->ol_d_id >= 1 && static_cast<size_t>(k->ol_d_id) <= NumDistrictsPerWarehouse());
+    INVARIANT(k->ol_number >= 1 && k->ol_number <= 15);
+#ifdef DISABLE_FIELD_SELECTION
+    INVARIANT(v->ol_i_id >= 1 && static_cast<size_t>(v->ol_i_id) <= NumItems());
+#endif
+  }
+};
+
+static string NameTokens[] =
+  {
+    string("BAR"),
+    string("OUGHT"),
+    string("ABLE"),
+    string("PRI"),
+    string("PRES"),
+    string("ESE"),
+    string("ANTI"),
+    string("CALLY"),
+    string("ATION"),
+    string("EING"),
+  };
+
+class tpcc_worker_mixin {
+public:
+
+  // only TPCC loaders need to call this- workers are automatically
+  // pinned by their worker id (which corresponds to warehouse id
+  // in TPCC)
+  //
+  // pins the *calling* thread
+  static void
+  PinToWarehouseId(unsigned int wid)
+  {
+    const unsigned int partid = PartitionId(wid);
+    ALWAYS_ASSERT(partid < nthreads);
+    const unsigned int pinid  = partid;
+    if (verbose)
+      cerr << "PinToWarehouseId(): coreid=" << coreid::core_id()
+           << " pinned to whse=" << wid << " (partid=" << partid << ")"
+           << endl;
+    rcu::s_instance.pin_current_thread(pinid);
+    rcu::s_instance.fault_region();
+  }
+
+  static inline uint32_t
+  GetCurrentTimeMillis()
+  {
+    //struct timeval tv;
+    //ALWAYS_ASSERT(gettimeofday(&tv, 0) == 0);
+    //return tv.tv_sec * 1000;
+
+    // XXX(stephentu): implement a scalable GetCurrentTimeMillis()
+    // for now, we just give each core an increasing number
+
+    static __thread uint32_t tl_hack = 0;
+    return tl_hack++;
+  }
+
+  // utils for generating random #s and strings
+
+  static inline ALWAYS_INLINE int
+  CheckBetweenInclusive(int v, int lower, int upper)
+  {
+    INVARIANT(v >= lower);
+    INVARIANT(v <= upper);
+    return v;
+  }
+
+  static inline ALWAYS_INLINE int
+  RandomNumber(fast_random &r, int min, int max)
+  {
+    return CheckBetweenInclusive((int) (r.next_uniform() * (max - min + 1) + min), min, max);
+  }
+
+  static inline ALWAYS_INLINE int
+  NonUniformRandom(fast_random &r, int A, int C, int min, int max)
+  {
+    return (((RandomNumber(r, 0, A) | RandomNumber(r, min, max)) + C) % (max - min + 1)) + min;
+  }
+
+  static inline ALWAYS_INLINE int
+  GetItemId(fast_random &r)
+  {
+    return CheckBetweenInclusive(
+        g_uniform_item_dist ?
+          RandomNumber(r, 1, NumItems()) :
+          NonUniformRandom(r, 8191, 7911, 1, NumItems()),
+        1, NumItems());
+  }
+
+  static inline ALWAYS_INLINE int
+  GetCustomerId(fast_random &r)
+  {
+    return CheckBetweenInclusive(NonUniformRandom(r, 1023, 259, 1, NumCustomersPerDistrict()), 1, NumCustomersPerDistrict());
+  }
+
+  // pick a number between [start, end)
+  static inline ALWAYS_INLINE unsigned
+  PickWarehouseId(fast_random &r, unsigned start, unsigned end)
+  {
+    INVARIANT(start < end);
+    const unsigned diff = end - start;
+    if (diff == 1)
+      return start;
+    return (r.next() % diff) + start;
+  }
+  // all tokens are at most 5 chars long
+  static const size_t CustomerLastNameMaxSize = 5 * 3;
+
+  static inline size_t
+  GetCustomerLastName(uint8_t *buf, fast_random &r, int num)
+  {
+    const string &s0 = NameTokens[num / 100];
+    const string &s1 = NameTokens[(num / 10) % 10];
+    const string &s2 = NameTokens[num % 10];
+    uint8_t *const begin = buf;
+    const size_t s0_sz = s0.size();
+    const size_t s1_sz = s1.size();
+    const size_t s2_sz = s2.size();
+    NDB_MEMCPY(buf, s0.data(), s0_sz); buf += s0_sz;
+    NDB_MEMCPY(buf, s1.data(), s1_sz); buf += s1_sz;
+    NDB_MEMCPY(buf, s2.data(), s2_sz); buf += s2_sz;
+    return buf - begin;
+  }
+
+  static inline ALWAYS_INLINE size_t
+  GetCustomerLastName(char *buf, fast_random &r, int num)
+  {
+    return GetCustomerLastName((uint8_t *) buf, r, num);
+  }
+
+  static inline string
+  GetCustomerLastName(fast_random &r, int num)
+  {
+    string ret;
+    ret.resize(CustomerLastNameMaxSize);
+    ret.resize(GetCustomerLastName((uint8_t *) &ret[0], r, num));
+    return ret;
+  }
+
+  static inline ALWAYS_INLINE string
+  GetNonUniformCustomerLastNameLoad(fast_random &r)
+  {
+    return GetCustomerLastName(r, NonUniformRandom(r, 255, 157, 0, 999));
+  }
+
+  static inline ALWAYS_INLINE size_t
+  GetNonUniformCustomerLastNameRun(uint8_t *buf, fast_random &r)
+  {
+    return GetCustomerLastName(buf, r, NonUniformRandom(r, 255, 223, 0, 999));
+  }
+
+  static inline ALWAYS_INLINE size_t
+  GetNonUniformCustomerLastNameRun(char *buf, fast_random &r)
+  {
+    return GetNonUniformCustomerLastNameRun((uint8_t *) buf, r);
+  }
+
+  static inline ALWAYS_INLINE string
+  GetNonUniformCustomerLastNameRun(fast_random &r)
+  {
+    return GetCustomerLastName(r, NonUniformRandom(r, 255, 223, 0, 999));
+  }
+
+  // following oltpbench, we really generate strings of len - 1...
+  static inline string
+  RandomStr(fast_random &r, uint len)
+  {
+    // this is a property of the oltpbench implementation...
+    if (!len)
+      return "";
+
+    uint i = 0;
+    string buf(len - 1, 0);
+    while (i < (len - 1)) {
+      const char c = (char) r.next_char();
+      // XXX(stephentu): oltpbench uses java's Character.isLetter(), which
+      // is a less restrictive filter than isalnum()
+      if (!isalnum(c))
+        continue;
+      buf[i++] = c;
+    }
+    return buf;
+  }
+
+  // RandomNStr() actually produces a string of length len
+  static inline string
+  RandomNStr(fast_random &r, uint len)
+  {
+    const char base = '0';
+    string buf(len, 0);
+    for (uint i = 0; i < len; i++)
+      buf[i] = (char)(base + (r.next() % 10));
+    return buf;
+  }
+
+};
+
+STATIC_COUNTER_DECL(scopedperf::tsc_ctr, tpcc_txn, tpcc_txn_cg)
+
+template <typename Database, bool AllowReadOnlyScans>
+class tpcc_worker : public bench_worker, public tpcc_worker_mixin {
+public:
+  tpcc_worker(unsigned int worker_id,
+              unsigned long seed, Database *db,
+              const tpcc_tables<Database> &tables,
+              spin_barrier *barrier_a, spin_barrier *barrier_b,
+              uint warehouse_id_start, uint warehouse_id_end)
+    : bench_worker(worker_id, true, seed, db,
+                   barrier_a, barrier_b),
+      tpcc_worker_mixin(),
+      tables(tables),
+      warehouse_id_start(warehouse_id_start),
+      warehouse_id_end(warehouse_id_end)
+  {
+    ALWAYS_ASSERT(g_disable_read_only_scans == !AllowReadOnlyScans);
+    INVARIANT(warehouse_id_start >= 1);
+    INVARIANT(warehouse_id_start <= NumWarehouses());
+    INVARIANT(warehouse_id_end > warehouse_id_start);
+    INVARIANT(warehouse_id_end <= (NumWarehouses() + 1));
+    NDB_MEMSET(&last_no_o_ids[0], 0, sizeof(last_no_o_ids));
+    if (verbose) {
+      cerr << "tpcc: worker id " << worker_id
+        << " => warehouses [" << warehouse_id_start
+        << ", " << warehouse_id_end << ")"
+        << endl;
+    }
+  }
+
+  // XXX(stephentu): tune this
+  static const size_t NMaxCustomerIdxScanElems = 512;
+
+  txn_result txn_new_order();
+
+  static txn_result
+  TxnNewOrder(bench_worker *w)
+  {
+    ANON_REGION("TxnNewOrder:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_new_order();
+  }
+
+  txn_result txn_delivery();
+
+  static txn_result
+  TxnDelivery(bench_worker *w)
+  {
+    ANON_REGION("TxnDelivery:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_delivery();
+  }
+
+  txn_result txn_payment();
+
+  static txn_result
+  TxnPayment(bench_worker *w)
+  {
+    ANON_REGION("TxnPayment:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_payment();
+  }
+
+  txn_result txn_order_status();
+
+  static txn_result
+  TxnOrderStatus(bench_worker *w)
+  {
+    ANON_REGION("TxnOrderStatus:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_order_status();
+  }
+
+  txn_result txn_stock_level();
+
+  static txn_result
+  TxnStockLevel(bench_worker *w)
+  {
+    ANON_REGION("TxnStockLevel:", &tpcc_txn_cg);
+    return static_cast<tpcc_worker *>(w)->txn_stock_level();
+  }
+
+  virtual workload_desc_vec
+  get_workload() const OVERRIDE
+  {
+    workload_desc_vec w;
+    // numbers from sigmod.csail.mit.edu:
+    //w.push_back(workload_desc("NewOrder", 1.0, TxnNewOrder)); // ~10k ops/sec
+    //w.push_back(workload_desc("Payment", 1.0, TxnPayment)); // ~32k ops/sec
+    //w.push_back(workload_desc("Delivery", 1.0, TxnDelivery)); // ~104k ops/sec
+    //w.push_back(workload_desc("OrderStatus", 1.0, TxnOrderStatus)); // ~33k ops/sec
+    //w.push_back(workload_desc("StockLevel", 1.0, TxnStockLevel)); // ~2k ops/sec
+    unsigned m = 0;
+    for (size_t i = 0; i < ARRAY_NELEMS(g_txn_workload_mix); i++)
+      m += g_txn_workload_mix[i];
+    ALWAYS_ASSERT(m == 100);
+    if (g_txn_workload_mix[0])
+      w.push_back(workload_desc("NewOrder", double(g_txn_workload_mix[0])/100.0, TxnNewOrder));
+    if (g_txn_workload_mix[1])
+      w.push_back(workload_desc("Payment", double(g_txn_workload_mix[1])/100.0, TxnPayment));
+    if (g_txn_workload_mix[2])
+      w.push_back(workload_desc("Delivery", double(g_txn_workload_mix[2])/100.0, TxnDelivery));
+    if (g_txn_workload_mix[3])
+      w.push_back(workload_desc("OrderStatus", double(g_txn_workload_mix[3])/100.0, TxnOrderStatus));
+    if (g_txn_workload_mix[4])
+      w.push_back(workload_desc("StockLevel", double(g_txn_workload_mix[4])/100.0, TxnStockLevel));
+    return w;
+  }
+
+protected:
+
+  virtual void
+  on_run_setup() OVERRIDE
+  {
+    if (!pin_cpus)
+      return;
+    const size_t a = worker_id % coreid::num_cpus_online();
+    const size_t b = a % nthreads;
+    rcu::s_instance.pin_current_thread(b);
+    rcu::s_instance.fault_region();
+  }
+
+private:
+  tpcc_tables<Database> tables;
+  const uint warehouse_id_start;
+  const uint warehouse_id_end;
+  int32_t last_no_o_ids[10]; // XXX(stephentu): hack
+};
+
+template <typename Database>
+class tpcc_warehouse_loader : public typed_bench_loader<Database>,
+                              public tpcc_worker_mixin {
+public:
+  tpcc_warehouse_loader(unsigned long seed,
+                        Database *db,
+                        const tpcc_tables<Database> &tables)
+    : typed_bench_loader<Database>(seed, db),
+      tpcc_worker_mixin(),
+      tables(tables)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    uint64_t warehouse_total_sz = 0, n_warehouses = 0;
+    try {
+      vector<warehouse::value> warehouses;
+      {
+        scoped_str_arena s_arena(this->arena);
+        typename Database::template
+          TransactionType<abstract_db::HINT_DEFAULT>::type txn(txn_flags, this->arena);
+        for (uint i = 1; i <= NumWarehouses(); i++) {
+          const warehouse::key k(i);
+
+          const string w_name = RandomStr(this->r, RandomNumber(this->r, 6, 10));
+          const string w_street_1 = RandomStr(this->r, RandomNumber(this->r, 10, 20));
+          const string w_street_2 = RandomStr(this->r, RandomNumber(this->r, 10, 20));
+          const string w_city = RandomStr(this->r, RandomNumber(this->r, 10, 20));
+          const string w_state = RandomStr(this->r, 3);
+          const string w_zip = "123456789";
+
+          warehouse::value v;
+          v.w_ytd = 300000;
+          v.w_tax = (float) RandomNumber(this->r, 0, 2000) / 10000.0;
+          v.w_name.assign(w_name);
+          v.w_street_1.assign(w_street_1);
+          v.w_street_2.assign(w_street_2);
+          v.w_city.assign(w_city);
+          v.w_state.assign(w_state);
+          v.w_zip.assign(w_zip);
+
+          checker::SanityCheckWarehouse(&k, &v);
+          const size_t sz = Size(v);
+          warehouse_total_sz += sz;
+          n_warehouses++;
+          tables.tbl_warehouse(i)->insert(txn, k, v);
+          warehouses.push_back(v);
+        }
+        ALWAYS_ASSERT(txn.commit());
+      }
+      {
+        scoped_str_arena s_arena(this->arena);
+        typename Database::template
+          TransactionType<abstract_db::HINT_DEFAULT>::type txn(txn_flags, this->arena);
+        for (uint i = 1; i <= NumWarehouses(); i++) {
+          const warehouse::key k(i);
+          warehouse::value v;
+          ALWAYS_ASSERT(tables.tbl_warehouse(i)->search(txn, k, v));
+          ALWAYS_ASSERT(warehouses[i - 1] == v);
+          checker::SanityCheckWarehouse(&k, &v);
+        }
+        ALWAYS_ASSERT(txn.commit());
+      }
+    } catch (typename Database::abort_exception_type &e) {
+      // shouldn't abort on loading!
+      ALWAYS_ASSERT(false);
+    }
+    if (verbose) {
+      cerr << "[INFO] finished loading warehouse" << endl;
+      cerr << "[INFO]   * average warehouse record length: "
+           << (double(warehouse_total_sz)/double(n_warehouses)) << " bytes" << endl;
+    }
+  }
+
+  tpcc_tables<Database> tables;
+};
+
+template <typename Database>
+class tpcc_item_loader : public typed_bench_loader<Database>,
+                         public tpcc_worker_mixin {
+public:
+  tpcc_item_loader(unsigned long seed,
+                   Database *db,
+                   const tpcc_tables<Database> &tables)
+    : typed_bench_loader<Database>(seed, db),
+      tpcc_worker_mixin(),
+      tables(tables)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    const ssize_t bsize = this->typed_db()->txn_max_batch_size();
+    auto txn = this->typed_db()->template new_txn<abstract_db::HINT_DEFAULT>(txn_flags, this->arena);
+    uint64_t total_sz = 0;
+    try {
+      for (uint i = 1; i <= NumItems(); i++) {
+        // items don't "belong" to a certain warehouse, so no pinning
+        const item::key k(i);
+
+        item::value v;
+        const string i_name = RandomStr(this->r, RandomNumber(this->r, 14, 24));
+        v.i_name.assign(i_name);
+        v.i_price = (float) RandomNumber(this->r, 100, 10000) / 100.0;
+        const int len = RandomNumber(this->r, 26, 50);
+        if (RandomNumber(this->r, 1, 100) > 10) {
+          const string i_data = RandomStr(this->r, len);
+          v.i_data.assign(i_data);
+        } else {
+          const int startOriginal = RandomNumber(this->r, 2, (len - 8));
+          const string i_data = RandomStr(this->r, startOriginal + 1) + "ORIGINAL" + RandomStr(this->r, len - startOriginal - 7);
+          v.i_data.assign(i_data);
+        }
+        v.i_im_id = RandomNumber(this->r, 1, 10000);
+
+        checker::SanityCheckItem(&k, &v);
+        const size_t sz = Size(v);
+        total_sz += sz;
+        // XXX: replicate items table across all NUMA nodes
+        tables.tbl_item(1)->insert(*txn, k, v); // this table is shared, so any partition is OK
+
+        if (bsize != -1 && !(i % bsize)) {
+          ALWAYS_ASSERT(txn->commit());
+          txn = this->typed_db()->template new_txn<abstract_db::HINT_DEFAULT>(txn_flags, this->arena);
+          this->arena.reset();
+        }
+      }
+      ALWAYS_ASSERT(txn->commit());
+    } catch (typename Database::abort_exception_type &e) {
+      // shouldn't abort on loading!
+      ALWAYS_ASSERT(false);
+    }
+    if (verbose) {
+      cerr << "[INFO] finished loading item" << endl;
+      cerr << "[INFO]   * average item record length: "
+           << (double(total_sz)/double(NumItems())) << " bytes" << endl;
+    }
+  }
+
+  tpcc_tables<Database> tables;
+};
+
+template <typename Database>
+class tpcc_stock_loader : public typed_bench_loader<Database>,
+                          public tpcc_worker_mixin {
+public:
+  tpcc_stock_loader(unsigned long seed,
+                    Database *db,
+                    const tpcc_tables<Database> &tables,
+                    ssize_t warehouse_id)
+    : typed_bench_loader<Database>(seed, db),
+      tpcc_worker_mixin(),
+      tables(tables),
+      warehouse_id(warehouse_id)
+  {
+    ALWAYS_ASSERT(warehouse_id == -1 ||
+                  (warehouse_id >= 1 &&
+                   static_cast<size_t>(warehouse_id) <= NumWarehouses()));
+  }
+
+protected:
+  virtual void
+  load()
+  {
+    uint64_t stock_total_sz = 0, n_stocks = 0;
+    const uint w_start = (warehouse_id == -1) ?
+      1 : static_cast<uint>(warehouse_id);
+    const uint w_end   = (warehouse_id == -1) ?
+      NumWarehouses() : static_cast<uint>(warehouse_id);
+
+    for (uint w = w_start; w <= w_end; w++) {
+      const size_t batchsize =
+        (this->typed_db()->txn_max_batch_size() == -1) ?
+          NumItems() : this->typed_db()->txn_max_batch_size();
+      const size_t nbatches = (batchsize > NumItems()) ? 1 : (NumItems() / batchsize);
+
+      if (pin_cpus)
+        PinToWarehouseId(w);
+
+      for (uint b = 0; b < nbatches;) {
+        scoped_str_arena s_arena(this->arena);
+        auto txn = this->typed_db()->template new_txn<abstract_db::HINT_DEFAULT>(txn_flags, this->arena);
+        try {
+          const size_t iend = std::min((b + 1) * batchsize + 1, NumItems());
+          for (uint i = (b * batchsize + 1); i <= iend; i++) {
+            const stock::key k(w, i);
+            const stock_data::key k_data(w, i);
+
+            stock::value v;
+            v.s_quantity = RandomNumber(this->r, 10, 100);
+            v.s_ytd = 0;
+            v.s_order_cnt = 0;
+            v.s_remote_cnt = 0;
+
+            stock_data::value v_data;
+            const int len = RandomNumber(this->r, 26, 50);
+            if (RandomNumber(this->r, 1, 100) > 10) {
+              const string s_data = RandomStr(this->r, len);
+              v_data.s_data.assign(s_data);
+            } else {
+              const int startOriginal = RandomNumber(this->r, 2, (len - 8));
+              const string s_data = RandomStr(this->r, startOriginal + 1) +
+                "ORIGINAL" + RandomStr(this->r, len - startOriginal - 7);
+              v_data.s_data.assign(s_data);
+            }
+            v_data.s_dist_01.assign(RandomStr(this->r, 24));
+            v_data.s_dist_02.assign(RandomStr(this->r, 24));
+            v_data.s_dist_03.assign(RandomStr(this->r, 24));
+            v_data.s_dist_04.assign(RandomStr(this->r, 24));
+            v_data.s_dist_05.assign(RandomStr(this->r, 24));
+            v_data.s_dist_06.assign(RandomStr(this->r, 24));
+            v_data.s_dist_07.assign(RandomStr(this->r, 24));
+            v_data.s_dist_08.assign(RandomStr(this->r, 24));
+            v_data.s_dist_09.assign(RandomStr(this->r, 24));
+            v_data.s_dist_10.assign(RandomStr(this->r, 24));
+
+            checker::SanityCheckStock(&k, &v);
+            const size_t sz = Size(v);
+            stock_total_sz += sz;
+            n_stocks++;
+            tables.tbl_stock(w)->insert(*txn, k, v);
+            tables.tbl_stock_data(w)->insert(*txn, k_data, v_data);
+          }
+          if (txn->commit()) {
+            b++;
+          } else {
+            if (verbose)
+              cerr << "[WARNING] stock loader loading abort" << endl;
+          }
+        } catch (typename Database::abort_exception_type &e) {
+          txn->abort();
+          ALWAYS_ASSERT(warehouse_id != -1);
+          if (verbose)
+            cerr << "[WARNING] stock loader loading abort" << endl;
+        }
+      }
+    }
+
+    if (verbose) {
+      if (warehouse_id == -1) {
+        cerr << "[INFO] finished loading stock" << endl;
+        cerr << "[INFO]   * average stock record length: "
+             << (double(stock_total_sz)/double(n_stocks)) << " bytes" << endl;
+      } else {
+        cerr << "[INFO] finished loading stock (w=" << warehouse_id << ")" << endl;
+      }
+    }
+  }
+
+private:
+  tpcc_tables<Database> tables;
+  ssize_t warehouse_id;
+};
+
+template <typename Database>
+class tpcc_district_loader : public typed_bench_loader<Database>,
+                             public tpcc_worker_mixin {
+public:
+  tpcc_district_loader(unsigned long seed,
+                       Database *db,
+                       const tpcc_tables<Database> &tables)
+    : typed_bench_loader<Database>(seed, db),
+      tpcc_worker_mixin(),
+      tables(tables)
+  {}
+
+protected:
+  virtual void
+  load()
+  {
+    const ssize_t bsize = this->typed_db()->txn_max_batch_size();
+    auto txn = this->typed_db()->template new_txn<abstract_db::HINT_DEFAULT>(txn_flags, this->arena);
+    uint64_t district_total_sz = 0, n_districts = 0;
+    try {
+      uint cnt = 0;
+      for (uint w = 1; w <= NumWarehouses(); w++) {
+        if (pin_cpus)
+          PinToWarehouseId(w);
+        for (uint d = 1; d <= NumDistrictsPerWarehouse(); d++, cnt++) {
+          const district::key k(w, d);
+
+          district::value v;
+          v.d_ytd = 30000;
+          v.d_tax = (float) (RandomNumber(this->r, 0, 2000) / 10000.0);
+          v.d_next_o_id = 3001;
+          v.d_name.assign(RandomStr(this->r, RandomNumber(this->r, 6, 10)));
+          v.d_street_1.assign(RandomStr(this->r, RandomNumber(this->r, 10, 20)));
+          v.d_street_2.assign(RandomStr(this->r, RandomNumber(this->r, 10, 20)));
+          v.d_city.assign(RandomStr(this->r, RandomNumber(this->r, 10, 20)));
+          v.d_state.assign(RandomStr(this->r, 3));
+          v.d_zip.assign("123456789");
+
+          checker::SanityCheckDistrict(&k, &v);
+          const size_t sz = Size(v);
+          district_total_sz += sz;
+          n_districts++;
+          tables.tbl_district(w)->insert(*txn, k, v);
+
+          if (bsize != -1 && !((cnt + 1) % bsize)) {
+            ALWAYS_ASSERT(txn->commit());
+            txn = this->typed_db()->template new_txn<abstract_db::HINT_DEFAULT>(txn_flags, this->arena);
+            this->arena.reset();
+          }
+        }
+      }
+      ALWAYS_ASSERT(txn->commit());
+    } catch (typename Database::abort_exception_type &e) {
+      // shouldn't abort on loading!
+      ALWAYS_ASSERT(false);
+    }
+    if (verbose) {
+      cerr << "[INFO] finished loading district" << endl;
+      cerr << "[INFO]   * average district record length: "
+           << (double(district_total_sz)/double(n_districts)) << " bytes" << endl;
+    }
+  }
+
+  tpcc_tables<Database> tables;
+};
+
+template <typename Database>
+class tpcc_customer_loader : public typed_bench_loader<Database>,
+                             public tpcc_worker_mixin {
+public:
+  tpcc_customer_loader(unsigned long seed,
+                       Database *db,
+                       const tpcc_tables<Database> &tables,
+                       ssize_t warehouse_id)
+    : typed_bench_loader<Database>(seed, db),
+      tpcc_worker_mixin(),
+      tables(tables),
+      warehouse_id(warehouse_id)
+  {
+    ALWAYS_ASSERT(warehouse_id == -1 ||
+                  (warehouse_id >= 1 &&
+                   static_cast<size_t>(warehouse_id) <= NumWarehouses()));
+  }
+
+protected:
+  virtual void
+  load()
+  {
+    const uint w_start = (warehouse_id == -1) ?
+      1 : static_cast<uint>(warehouse_id);
+    const uint w_end   = (warehouse_id == -1) ?
+      NumWarehouses() : static_cast<uint>(warehouse_id);
+    const size_t batchsize =
+      (this->typed_db()->txn_max_batch_size() == -1) ?
+        NumCustomersPerDistrict() : this->typed_db()->txn_max_batch_size();
+    const size_t nbatches =
+      (batchsize > NumCustomersPerDistrict()) ?
+        1 : (NumCustomersPerDistrict() / batchsize);
+    cerr << "num batches: " << nbatches << endl;
+
+    uint64_t total_sz = 0;
+
+    for (uint w = w_start; w <= w_end; w++) {
+      if (pin_cpus)
+        PinToWarehouseId(w);
+      for (uint d = 1; d <= NumDistrictsPerWarehouse(); d++) {
+        for (uint batch = 0; batch < nbatches;) {
+          scoped_str_arena s_arena(this->arena);
+          typename Database::template TransactionType<abstract_db::HINT_DEFAULT>::type txn(txn_flags, this->arena);
+          const size_t cstart = batch * batchsize;
+          const size_t cend = std::min((batch + 1) * batchsize, NumCustomersPerDistrict());
+          try {
+            for (uint cidx0 = cstart; cidx0 < cend; cidx0++) {
+              const uint c = cidx0 + 1;
+              const customer::key k(w, d, c);
+
+              customer::value v;
+              v.c_discount = (float) (RandomNumber(this->r, 1, 5000) / 10000.0);
+              if (RandomNumber(this->r, 1, 100) <= 10)
+                v.c_credit.assign("BC");
+              else
+                v.c_credit.assign("GC");
+
+              if (c <= 1000)
+                v.c_last.assign(GetCustomerLastName(this->r, c - 1));
+              else
+                v.c_last.assign(GetNonUniformCustomerLastNameLoad(this->r));
+
+              v.c_first.assign(RandomStr(this->r, RandomNumber(this->r, 8, 16)));
+              v.c_credit_lim = 50000;
+
+              v.c_balance = -10;
+              v.c_ytd_payment = 10;
+              v.c_payment_cnt = 1;
+              v.c_delivery_cnt = 0;
+
+              v.c_street_1.assign(RandomStr(this->r, RandomNumber(this->r, 10, 20)));
+              v.c_street_2.assign(RandomStr(this->r, RandomNumber(this->r, 10, 20)));
+              v.c_city.assign(RandomStr(this->r, RandomNumber(this->r, 10, 20)));
+              v.c_state.assign(RandomStr(this->r, 3));
+              v.c_zip.assign(RandomNStr(this->r, 4) + "11111");
+              v.c_phone.assign(RandomNStr(this->r, 16));
+              v.c_since = GetCurrentTimeMillis();
+              v.c_middle.assign("OE");
+              v.c_data.assign(RandomStr(this->r, RandomNumber(this->r, 300, 500)));
+
+              checker::SanityCheckCustomer(&k, &v);
+              const size_t sz = Size(v);
+              total_sz += sz;
+              tables.tbl_customer(w)->insert(txn, k, v);
+
+              // customer name index
+              const customer_name_idx::key k_idx(k.c_w_id, k.c_d_id, v.c_last.str(true), v.c_first.str(true));
+              const customer_name_idx::value v_idx(k.c_id);
+
+              // index structure is:
+              // (c_w_id, c_d_id, c_last, c_first) -> (c_id)
+
+              tables.tbl_customer_name_idx(w)->insert(txn, k_idx, v_idx);
+
+              history::key k_hist;
+              k_hist.h_c_id = c;
+              k_hist.h_c_d_id = d;
+              k_hist.h_c_w_id = w;
+              k_hist.h_d_id = d;
+              k_hist.h_w_id = w;
+              k_hist.h_date = GetCurrentTimeMillis();
+
+              history::value v_hist;
+              v_hist.h_amount = 10;
+              v_hist.h_data.assign(RandomStr(this->r, RandomNumber(this->r, 10, 24)));
+
+              tables.tbl_history(w)->insert(txn, k_hist, v_hist);
+            }
+            if (txn.commit()) {
+              batch++;
+            } else {
+              txn.abort();
+              if (verbose)
+                cerr << "[WARNING] customer loader loading abort" << endl;
+            }
+          } catch (typename Database::abort_exception_type &e) {
+            txn.abort();
+            if (verbose)
+              cerr << "[WARNING] customer loader loading abort" << endl;
+          }
+        }
+      }
+    }
+
+    if (verbose) {
+      if (warehouse_id == -1) {
+        cerr << "[INFO] finished loading customer" << endl;
+        cerr << "[INFO]   * average customer record length: "
+             << (double(total_sz)/double(NumWarehouses()*NumDistrictsPerWarehouse()*NumCustomersPerDistrict()))
+             << " bytes " << endl;
+      } else {
+        cerr << "[INFO] finished loading customer (w=" << warehouse_id << ")" << endl;
+      }
+    }
+  }
+
+private:
+  tpcc_tables<Database> tables;
+  ssize_t warehouse_id;
+};
+
+template <typename Database>
+class tpcc_order_loader : public typed_bench_loader<Database>,
+                          public tpcc_worker_mixin {
+public:
+  tpcc_order_loader(unsigned long seed,
+                    Database *db,
+                    const tpcc_tables<Database> &tables,
+                    ssize_t warehouse_id)
+    : typed_bench_loader<Database>(seed, db),
+      tpcc_worker_mixin(),
+      tables(tables),
+      warehouse_id(warehouse_id)
+  {
+    ALWAYS_ASSERT(warehouse_id == -1 ||
+                  (warehouse_id >= 1 &&
+                   static_cast<size_t>(warehouse_id) <= NumWarehouses()));
+  }
+
+protected:
+  virtual void
+  load()
+  {
+    uint64_t order_line_total_sz = 0, n_order_lines = 0;
+    uint64_t oorder_total_sz = 0, n_oorders = 0;
+    uint64_t new_order_total_sz = 0, n_new_orders = 0;
+
+    const uint w_start = (warehouse_id == -1) ?
+      1 : static_cast<uint>(warehouse_id);
+    const uint w_end   = (warehouse_id == -1) ?
+      NumWarehouses() : static_cast<uint>(warehouse_id);
+
+    for (uint w = w_start; w <= w_end; w++) {
+      if (pin_cpus)
+        PinToWarehouseId(w);
+      for (uint d = 1; d <= NumDistrictsPerWarehouse(); d++) {
+        set<uint> c_ids_s;
+        vector<uint> c_ids;
+        while (c_ids.size() != NumCustomersPerDistrict()) {
+          const auto x = (this->r.next() % NumCustomersPerDistrict()) + 1;
+          if (c_ids_s.count(x))
+            continue;
+          c_ids_s.insert(x);
+          c_ids.emplace_back(x);
+        }
+        for (uint c = 1; c <= NumCustomersPerDistrict();) {
+          scoped_str_arena s_arena(this->arena);
+          typename Database::template TransactionType<abstract_db::HINT_DEFAULT>::type txn(txn_flags, this->arena);
+          try {
+            const oorder::key k_oo(w, d, c);
+
+            oorder::value v_oo;
+            v_oo.o_c_id = c_ids[c - 1];
+            if (k_oo.o_id < 2101)
+              v_oo.o_carrier_id = RandomNumber(this->r, 1, 10);
+            else
+              v_oo.o_carrier_id = 0;
+            v_oo.o_ol_cnt = RandomNumber(this->r, 5, 15);
+            v_oo.o_all_local = 1;
+            v_oo.o_entry_d = GetCurrentTimeMillis();
+
+            checker::SanityCheckOOrder(&k_oo, &v_oo);
+            const size_t sz = Size(v_oo);
+            oorder_total_sz += sz;
+            n_oorders++;
+            tables.tbl_oorder(w)->insert(txn, k_oo, v_oo);
+
+            const oorder_c_id_idx::key k_oo_idx(k_oo.o_w_id, k_oo.o_d_id, v_oo.o_c_id, k_oo.o_id);
+            const oorder_c_id_idx::value v_oo_idx(0);
+
+            tables.tbl_oorder_c_id_idx(w)->insert(txn, k_oo_idx, v_oo_idx);
+
+            if (c >= 2101) {
+              const new_order::key k_no(w, d, c);
+              const new_order::value v_no;
+
+              checker::SanityCheckNewOrder(&k_no, &v_no);
+              const size_t sz = Size(v_no);
+              new_order_total_sz += sz;
+              n_new_orders++;
+              tables.tbl_new_order(w)->insert(txn, k_no, v_no);
+            }
+
+            for (uint l = 1; l <= uint(v_oo.o_ol_cnt); l++) {
+              const order_line::key k_ol(w, d, c, l);
+
+              order_line::value v_ol;
+              v_ol.ol_i_id = RandomNumber(this->r, 1, 100000);
+              if (k_ol.ol_o_id < 2101) {
+                v_ol.ol_delivery_d = v_oo.o_entry_d;
+                v_ol.ol_amount = 0;
+              } else {
+                v_ol.ol_delivery_d = 0;
+                // random within [0.01 .. 9,999.99]
+                v_ol.ol_amount = (float) (RandomNumber(this->r, 1, 999999) / 100.0);
+              }
+
+              v_ol.ol_supply_w_id = k_ol.ol_w_id;
+              v_ol.ol_quantity = 5;
+              // v_ol.ol_dist_info comes from stock_data(ol_supply_w_id, ol_o_id)
+              //v_ol.ol_dist_info = RandomStr(this->r, 24);
+
+              checker::SanityCheckOrderLine(&k_ol, &v_ol);
+              const size_t sz = Size(v_ol);
+              order_line_total_sz += sz;
+              n_order_lines++;
+              tables.tbl_order_line(w)->insert(txn, k_ol, v_ol);
+            }
+            if (txn.commit()) {
+              c++;
+            } else {
+              txn.abort();
+              ALWAYS_ASSERT(warehouse_id != -1);
+              if (verbose)
+                cerr << "[WARNING] order loader loading abort" << endl;
+            }
+          } catch (typename Database::abort_exception_type &e) {
+            txn.abort();
+            ALWAYS_ASSERT(warehouse_id != -1);
+            if (verbose)
+              cerr << "[WARNING] order loader loading abort" << endl;
+          }
+        }
+      }
+    }
+
+    if (verbose) {
+      if (warehouse_id == -1) {
+        cerr << "[INFO] finished loading order" << endl;
+        cerr << "[INFO]   * average order_line record length: "
+             << (double(order_line_total_sz)/double(n_order_lines)) << " bytes" << endl;
+        cerr << "[INFO]   * average oorder record length: "
+             << (double(oorder_total_sz)/double(n_oorders)) << " bytes" << endl;
+        cerr << "[INFO]   * average new_order record length: "
+             << (double(new_order_total_sz)/double(n_new_orders)) << " bytes" << endl;
+      } else {
+        cerr << "[INFO] finished loading order (w=" << warehouse_id << ")" << endl;
+      }
+    }
+  }
+
+private:
+  tpcc_tables<Database> tables;
+  ssize_t warehouse_id;
+};
+
+static event_counter evt_tpcc_cross_partition_new_order_txns("tpcc_cross_partition_new_order_txns");
+static event_counter evt_tpcc_cross_partition_payment_txns("tpcc_cross_partition_payment_txns");
+
+template <typename Database, bool AllowReadOnlyScans>
+typename tpcc_worker<Database, AllowReadOnlyScans>::txn_result
+tpcc_worker<Database, AllowReadOnlyScans>::txn_new_order()
+{
+  const uint warehouse_id = PickWarehouseId(this->r, warehouse_id_start, warehouse_id_end);
+  const uint districtID = RandomNumber(this->r, 1, 10);
+  const uint customerID = GetCustomerId(r);
+  const uint numItems = RandomNumber(this->r, 5, 15);
+  uint itemIDs[15], supplierWarehouseIDs[15], orderQuantities[15];
+  bool allLocal = true;
+  for (uint i = 0; i < numItems; i++) {
+    itemIDs[i] = GetItemId(r);
+    if (likely(g_disable_xpartition_txn ||
+               NumWarehouses() == 1 ||
+               RandomNumber(this->r, 1, 100) > g_new_order_remote_item_pct)) {
+      supplierWarehouseIDs[i] = warehouse_id;
+    } else {
+      do {
+       supplierWarehouseIDs[i] = RandomNumber(this->r, 1, NumWarehouses());
+      } while (supplierWarehouseIDs[i] == warehouse_id);
+      allLocal = false;
+    }
+    orderQuantities[i] = RandomNumber(this->r, 1, 10);
+  }
+  INVARIANT(!g_disable_xpartition_txn || allLocal);
+  if (!allLocal)
+    ++evt_tpcc_cross_partition_new_order_txns;
+
+  // XXX(stephentu): implement rollback
+  //
+  // worst case txn profile:
+  //   1 customer get
+  //   1 warehouse get
+  //   1 district get
+  //   1 new_order insert
+  //   1 district put
+  //   1 oorder insert
+  //   1 oorder_cid_idx insert
+  //   15 times:
+  //      1 item get
+  //      1 stock get
+  //      1 stock put
+  //      1 order_line insert
+  //
+  // output from txn counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 0
+  //   max_read_set_size : 15
+  //   max_write_set_size : 15
+  //   num_txn_contexts : 9
+  typename Database::template TransactionType<abstract_db::HINT_TPCC_NEW_ORDER>::type txn(txn_flags, this->arena);
+  scoped_str_arena s_arena(this->arena);
+  scoped_multilock<spinlock> mlock;
+  if (g_enable_partition_locks) {
+    if (allLocal) {
+      mlock.enq(LockForPartition(warehouse_id));
+    } else {
+      small_unordered_map<unsigned int, bool, 64> lockset;
+      mlock.enq(LockForPartition(warehouse_id));
+      lockset[PartitionId(warehouse_id)] = 1;
+      for (uint i = 0; i < numItems; i++) {
+        if (lockset.find(PartitionId(supplierWarehouseIDs[i])) == lockset.end()) {
+          mlock.enq(LockForPartition(supplierWarehouseIDs[i]));
+          lockset[PartitionId(supplierWarehouseIDs[i])] = 1;
+        }
+      }
+    }
+    mlock.multilock();
+  }
+  try {
+    ssize_t ret = 0;
+    const customer::key k_c(warehouse_id, districtID, customerID);
+    customer::value v_c;
+    ALWAYS_ASSERT(
+        tables.tbl_customer(warehouse_id)->search(txn, k_c, v_c,
+          GUARDED_FIELDS(
+            customer::value::c_discount_field,
+            customer::value::c_last_field,
+            customer::value::c_credit_field)));
+    checker::SanityCheckCustomer(&k_c, &v_c);
+
+    const warehouse::key k_w(warehouse_id);
+    warehouse::value v_w;
+    ALWAYS_ASSERT(
+        tables.tbl_warehouse(warehouse_id)->search(txn, k_w, v_w,
+          GUARDED_FIELDS(warehouse::value::w_tax_field)));
+    checker::SanityCheckWarehouse(&k_w, &v_w);
+
+    const district::key k_d(warehouse_id, districtID);
+    district::value v_d;
+    ALWAYS_ASSERT(
+        tables.tbl_district(warehouse_id)->search(txn, k_d, v_d,
+          GUARDED_FIELDS(
+            district::value::d_next_o_id_field,
+            district::value::d_tax_field)));
+    checker::SanityCheckDistrict(&k_d, &v_d);
+
+    const uint64_t my_next_o_id = g_new_order_fast_id_gen ?
+        FastNewOrderIdGen(warehouse_id, districtID) : v_d.d_next_o_id;
+
+    const new_order::key k_no(warehouse_id, districtID, my_next_o_id);
+    const new_order::value v_no;
+    const size_t new_order_sz = Size(v_no);
+    tables.tbl_new_order(warehouse_id)->insert(txn, k_no, v_no);
+    ret += new_order_sz;
+
+    if (!g_new_order_fast_id_gen) {
+      v_d.d_next_o_id++;
+      tables.tbl_district(warehouse_id)->put(txn, k_d, v_d,
+          GUARDED_FIELDS(district::value::d_next_o_id_field));
+    }
+
+    const oorder::key k_oo(warehouse_id, districtID, k_no.no_o_id);
+    oorder::value v_oo;
+    v_oo.o_c_id = int32_t(customerID);
+    v_oo.o_carrier_id = 0; // seems to be ignored
+    v_oo.o_ol_cnt = int8_t(numItems);
+    v_oo.o_all_local = allLocal;
+    v_oo.o_entry_d = GetCurrentTimeMillis();
+
+    const size_t oorder_sz = Size(v_oo);
+    tables.tbl_oorder(warehouse_id)->insert(txn, k_oo, v_oo);
+    ret += oorder_sz;
+
+    const oorder_c_id_idx::key k_oo_idx(warehouse_id, districtID, customerID, k_no.no_o_id);
+    const oorder_c_id_idx::value v_oo_idx(0);
+
+    tables.tbl_oorder_c_id_idx(warehouse_id)->insert(txn, k_oo_idx, v_oo_idx);
+
+    for (uint ol_number = 1; ol_number <= numItems; ol_number++) {
+      const uint ol_supply_w_id = supplierWarehouseIDs[ol_number - 1];
+      const uint ol_i_id = itemIDs[ol_number - 1];
+      const uint ol_quantity = orderQuantities[ol_number - 1];
+
+      const item::key k_i(ol_i_id);
+      item::value v_i;
+      ALWAYS_ASSERT(tables.tbl_item(1)->search(txn, k_i, v_i,
+            GUARDED_FIELDS(
+              item::value::i_price_field,
+              item::value::i_name_field,
+              item::value::i_data_field)));
+      checker::SanityCheckItem(&k_i, &v_i);
+
+      const stock::key k_s(ol_supply_w_id, ol_i_id);
+      stock::value v_s;
+      ALWAYS_ASSERT(tables.tbl_stock(ol_supply_w_id)->search(txn, k_s, v_s));
+      checker::SanityCheckStock(&k_s, &v_s);
+
+      stock::value v_s_new(v_s);
+      if (v_s_new.s_quantity - ol_quantity >= 10)
+        v_s_new.s_quantity -= ol_quantity;
+      else
+        v_s_new.s_quantity += -int32_t(ol_quantity) + 91;
+      v_s_new.s_ytd += ol_quantity;
+      v_s_new.s_remote_cnt += (ol_supply_w_id == warehouse_id) ? 0 : 1;
+
+      tables.tbl_stock(ol_supply_w_id)->put(txn, k_s, v_s_new);
+
+      const order_line::key k_ol(warehouse_id, districtID, k_no.no_o_id, ol_number);
+      order_line::value v_ol;
+      v_ol.ol_i_id = int32_t(ol_i_id);
+      v_ol.ol_delivery_d = 0; // not delivered yet
+      v_ol.ol_amount = float(ol_quantity) * v_i.i_price;
+      v_ol.ol_supply_w_id = int32_t(ol_supply_w_id);
+      v_ol.ol_quantity = int8_t(ol_quantity);
+
+      const size_t order_line_sz = Size(v_ol);
+      tables.tbl_order_line(warehouse_id)->insert(txn, k_ol, v_ol);
+      ret += order_line_sz;
+    }
+
+    //measure_txn_counters(txn, "txn_new_order");
+    if (likely(txn.commit()))
+      return txn_result(true, ret);
+  } catch (typename Database::abort_exception_type &e) {
+    txn.abort();
+  }
+  return txn_result(false, 0);
+}
+
+template <typename Database>
+class new_order_scan_callback : public
+  Database::template IndexType<schema<new_order>>::type::search_range_callback {
+public:
+  new_order_scan_callback() : invoked(false) {}
+  virtual bool
+  invoke(const new_order::key &key, const new_order::value &value) OVERRIDE
+  {
+    INVARIANT(!invoked);
+    k_no = key;
+    invoked = true;
+#ifdef CHECK_INVARIANTS
+    checker::SanityCheckNewOrder(&key, &value);
+#endif
+    return false;
+  }
+  inline const new_order::key &
+  get_key() const
+  {
+    return k_no;
+  }
+  inline bool was_invoked() const { return invoked; }
+private:
+  new_order::key k_no;
+  bool invoked;
+};
+
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, delivery_probe0_tod, delivery_probe0_cg)
+
+template <typename Database, bool AllowReadOnlyScans>
+typename tpcc_worker<Database, AllowReadOnlyScans>::txn_result
+tpcc_worker<Database, AllowReadOnlyScans>::txn_delivery()
+{
+  const uint warehouse_id = PickWarehouseId(this->r, warehouse_id_start, warehouse_id_end);
+  const uint o_carrier_id = RandomNumber(this->r, 1, NumDistrictsPerWarehouse());
+  const uint32_t ts = GetCurrentTimeMillis();
+
+  // worst case txn profile:
+  //   10 times:
+  //     1 new_order scan node
+  //     1 oorder get
+  //     2 order_line scan nodes
+  //     15 order_line puts
+  //     1 new_order remove
+  //     1 oorder put
+  //     1 customer get
+  //     1 customer put
+  //
+  // output from counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 21
+  //   max_read_set_size : 133
+  //   max_write_set_size : 133
+  //   num_txn_contexts : 4
+  typename Database::template TransactionType<abstract_db::HINT_TPCC_DELIVERY>::type txn(txn_flags, this->arena);
+  scoped_str_arena s_arena(this->arena);
+  scoped_lock_guard<spinlock> slock(
+      g_enable_partition_locks ? &LockForPartition(warehouse_id) : nullptr);
+  try {
+    ssize_t ret = 0;
+    for (uint d = 1; d <= NumDistrictsPerWarehouse(); d++) {
+      const new_order::key k_no_0(warehouse_id, d, last_no_o_ids[d - 1]);
+      const new_order::key k_no_1(warehouse_id, d, numeric_limits<int32_t>::max());
+      new_order_scan_callback<Database> new_order_c;
+      {
+        ANON_REGION("DeliverNewOrderScan:", &delivery_probe0_cg);
+        tables.tbl_new_order(warehouse_id)->search_range_call(
+            txn, k_no_0, &k_no_1, new_order_c);
+      }
+
+      const new_order::key &k_no = new_order_c.get_key();
+      if (unlikely(!new_order_c.was_invoked()))
+          continue;
+      last_no_o_ids[d - 1] = k_no.no_o_id + 1; // XXX: update last seen
+
+      const oorder::key k_oo(warehouse_id, d, k_no.no_o_id);
+      oorder::value v_oo;
+      if (unlikely(!tables.tbl_oorder(warehouse_id)->search(txn, k_oo, v_oo,
+              GUARDED_FIELDS(
+                oorder::value::o_c_id_field,
+                oorder::value::o_carrier_id_field)))) {
+        // even if we read the new order entry, there's no guarantee
+        // we will read the oorder entry: in this case the txn will abort,
+        // but we're simply bailing out early
+        txn.abort();
+        return txn_result(false, 0);
+      }
+      checker::SanityCheckOOrder(&k_oo, &v_oo);
+
+      // never more than 15 order_lines per order
+      static_limit_callback<typename Database::template IndexType<schema<order_line>>::type, 15, false> c;
+      const order_line::key k_oo_0(warehouse_id, d, k_no.no_o_id, 0);
+      const order_line::key k_oo_1(warehouse_id, d, k_no.no_o_id, numeric_limits<int32_t>::max());
+
+      // XXX(stephentu): mutable scans would help here
+      tables.tbl_order_line(warehouse_id)->search_range_call(
+          txn, k_oo_0, &k_oo_1, c, false,
+          GUARDED_FIELDS(
+            order_line::value::ol_amount_field,
+            order_line::value::ol_delivery_d_field));
+      float sum = 0.0;
+      for (size_t i = 0; i < c.size(); i++) {
+#if defined(CHECK_INVARIANTS) && defined(DISABLE_FIELD_SELECTION)
+        checker::SanityCheckOrderLine(&c.key(i), &c.value(i));
+#endif
+        sum += c.value(i).ol_amount;
+        c.value(i).ol_delivery_d = ts;
+        tables.tbl_order_line(warehouse_id)->put(txn, c.key(i), c.value(i),
+            GUARDED_FIELDS(order_line::value::ol_delivery_d_field));
+      }
+
+      // delete new order
+      tables.tbl_new_order(warehouse_id)->remove(txn, k_no);
+      ret -= 0 /*new_order_c.get_value_size()*/;
+
+      // update oorder
+      v_oo.o_carrier_id = o_carrier_id;
+      tables.tbl_oorder(warehouse_id)->put(txn, k_oo, v_oo,
+          GUARDED_FIELDS(oorder::value::o_carrier_id_field));
+
+      const uint c_id = v_oo.o_c_id;
+      const float ol_total = sum;
+
+      // update customer
+      const customer::key k_c(warehouse_id, d, c_id);
+      customer::value v_c;
+      ALWAYS_ASSERT(tables.tbl_customer(warehouse_id)->search(txn, k_c, v_c,
+            GUARDED_FIELDS(customer::value::c_balance_field)));
+      v_c.c_balance += ol_total;
+      tables.tbl_customer(warehouse_id)->put(txn, k_c, v_c,
+            GUARDED_FIELDS(customer::value::c_balance_field));
+    }
+    //measure_txn_counters(txn, "txn_delivery");
+    if (likely(txn.commit()))
+      return txn_result(true, ret);
+  } catch (typename Database::abort_exception_type &e) {
+    txn.abort();
+  }
+  return txn_result(false, 0);
+}
+
+static event_avg_counter evt_avg_cust_name_idx_scan_size("avg_cust_name_idx_scan_size");
+
+template <typename Database, bool AllowReadOnlyScans>
+typename tpcc_worker<Database, AllowReadOnlyScans>::txn_result
+tpcc_worker<Database, AllowReadOnlyScans>::txn_payment()
+{
+  const uint warehouse_id = PickWarehouseId(this->r, warehouse_id_start, warehouse_id_end);
+  const uint districtID = RandomNumber(this->r, 1, NumDistrictsPerWarehouse());
+  uint customerDistrictID, customerWarehouseID;
+  if (likely(g_disable_xpartition_txn ||
+             NumWarehouses() == 1 ||
+             RandomNumber(this->r, 1, 100) <= 85)) {
+    customerDistrictID = districtID;
+    customerWarehouseID = warehouse_id;
+  } else {
+    customerDistrictID = RandomNumber(this->r, 1, NumDistrictsPerWarehouse());
+    do {
+      customerWarehouseID = RandomNumber(this->r, 1, NumWarehouses());
+    } while (customerWarehouseID == warehouse_id);
+  }
+  const float paymentAmount = (float) (RandomNumber(this->r, 100, 500000) / 100.0);
+  const uint32_t ts = GetCurrentTimeMillis();
+  INVARIANT(!g_disable_xpartition_txn || customerWarehouseID == warehouse_id);
+
+  // output from txn counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 10
+  //   max_read_set_size : 71
+  //   max_write_set_size : 1
+  //   num_txn_contexts : 5
+  typename Database::template TransactionType<abstract_db::HINT_TPCC_PAYMENT>::type txn(txn_flags, this->arena);
+  scoped_str_arena s_arena(this->arena);
+  scoped_multilock<spinlock> mlock;
+  if (g_enable_partition_locks) {
+    mlock.enq(LockForPartition(warehouse_id));
+    if (PartitionId(customerWarehouseID) != PartitionId(warehouse_id))
+      mlock.enq(LockForPartition(customerWarehouseID));
+    mlock.multilock();
+  }
+  if (customerWarehouseID != warehouse_id)
+    ++evt_tpcc_cross_partition_payment_txns;
+  try {
+    ssize_t ret = 0;
+
+    const warehouse::key k_w(warehouse_id);
+    warehouse::value v_w;
+    ALWAYS_ASSERT(
+        tables.tbl_warehouse(warehouse_id)->search(txn, k_w, v_w, GUARDED_FIELDS(
+            warehouse::value::w_ytd_field,
+            warehouse::value::w_street_1_field,
+            warehouse::value::w_street_1_field,
+            warehouse::value::w_city_field,
+            warehouse::value::w_state_field,
+            warehouse::value::w_zip_field,
+            warehouse::value::w_name_field)));
+    checker::SanityCheckWarehouse(&k_w, &v_w);
+
+    v_w.w_ytd += paymentAmount;
+    tables.tbl_warehouse(warehouse_id)->put(txn, k_w, v_w, GUARDED_FIELDS(warehouse::value::w_ytd_field));
+
+    const district::key k_d(warehouse_id, districtID);
+    district::value v_d;
+    ALWAYS_ASSERT(tables.tbl_district(warehouse_id)->search(txn, k_d, v_d, GUARDED_FIELDS(
+            district::value::d_ytd_field,
+            district::value::d_street_1_field,
+            district::value::d_street_1_field,
+            district::value::d_city_field,
+            district::value::d_state_field,
+            district::value::d_zip_field,
+            district::value::d_name_field)));
+    checker::SanityCheckDistrict(&k_d, &v_d);
+
+    v_d.d_ytd += paymentAmount;
+    tables.tbl_district(warehouse_id)->put(txn, k_d, v_d, GUARDED_FIELDS(district::value::d_ytd_field));
+
+    customer::key k_c;
+    customer::value v_c;
+    if (RandomNumber(this->r, 1, 100) <= 60) {
+      // cust by name
+      uint8_t lastname_buf[CustomerLastNameMaxSize + 1];
+      static_assert(sizeof(lastname_buf) == 16, "XX");
+      NDB_MEMSET(lastname_buf, 0, sizeof(lastname_buf));
+      GetNonUniformCustomerLastNameRun(lastname_buf, r);
+
+      static const string zeros(16, 0);
+      static const string ones(16, 255);
+
+      customer_name_idx::key k_c_idx_0;
+      k_c_idx_0.c_w_id = customerWarehouseID;
+      k_c_idx_0.c_d_id = customerDistrictID;
+      k_c_idx_0.c_last.assign((const char *) lastname_buf, 16);
+      k_c_idx_0.c_first.assign(zeros);
+
+      customer_name_idx::key k_c_idx_1;
+      k_c_idx_1.c_w_id = customerWarehouseID;
+      k_c_idx_1.c_d_id = customerDistrictID;
+      k_c_idx_1.c_last.assign((const char *) lastname_buf, 16);
+      k_c_idx_1.c_first.assign(ones);
+
+      // probably a safe bet for now
+      bytes_static_limit_callback<
+        typename Database::template IndexType<schema<customer_name_idx>>::type,
+        NMaxCustomerIdxScanElems, true> c(s_arena.get());
+      tables.tbl_customer_name_idx(customerWarehouseID)->bytes_search_range_call(
+        txn, k_c_idx_0, &k_c_idx_1, c);
+      ALWAYS_ASSERT(c.size() > 0);
+      INVARIANT(c.size() < NMaxCustomerIdxScanElems); // we should detect this
+      int index = c.size() / 2;
+      if (c.size() % 2 == 0)
+        index--;
+      evt_avg_cust_name_idx_scan_size.offer(c.size());
+
+      customer_name_idx::value v_c_idx_temp;
+      const customer_name_idx::value *v_c_idx = Decode(c.value(index), v_c_idx_temp);
+
+      k_c.c_w_id = customerWarehouseID;
+      k_c.c_d_id = customerDistrictID;
+      k_c.c_id = v_c_idx->c_id;
+      ALWAYS_ASSERT(tables.tbl_customer(customerWarehouseID)->search(txn, k_c, v_c));
+    } else {
+      // cust by ID
+      const uint customerID = GetCustomerId(r);
+      k_c.c_w_id = customerWarehouseID;
+      k_c.c_d_id = customerDistrictID;
+      k_c.c_id = customerID;
+      ALWAYS_ASSERT(tables.tbl_customer(customerWarehouseID)->search(txn, k_c, v_c));
+    }
+    checker::SanityCheckCustomer(&k_c, &v_c);
+
+    v_c.c_balance -= paymentAmount;
+    v_c.c_ytd_payment += paymentAmount;
+    v_c.c_payment_cnt++;
+    if (strncmp(v_c.c_credit.data(), "BC", 2) == 0) {
+      char buf[501];
+      int n = snprintf(buf, sizeof(buf), "%d %d %d %d %d %f | %s",
+                       k_c.c_id,
+                       k_c.c_d_id,
+                       k_c.c_w_id,
+                       districtID,
+                       warehouse_id,
+                       paymentAmount,
+                       v_c.c_data.c_str());
+      v_c.c_data.resize_junk(
+          min(static_cast<size_t>(n), v_c.c_data.max_size()));
+      NDB_MEMCPY((void *) v_c.c_data.data(), &buf[0], v_c.c_data.size());
+    }
+
+    tables.tbl_customer(customerWarehouseID)->put(txn, k_c, v_c);
+
+    const history::key k_h(k_c.c_d_id, k_c.c_w_id, k_c.c_id, districtID, warehouse_id, ts);
+    history::value v_h;
+    v_h.h_amount = paymentAmount;
+    v_h.h_data.resize_junk(v_h.h_data.max_size());
+    int n = snprintf((char *) v_h.h_data.data(), v_h.h_data.max_size() + 1,
+                     "%.10s    %.10s",
+                     v_w.w_name.c_str(),
+                     v_d.d_name.c_str());
+    v_h.h_data.resize_junk(min(static_cast<size_t>(n), v_h.h_data.max_size()));
+
+    const size_t history_sz = Size(v_h);
+    tables.tbl_history(warehouse_id)->insert(txn, k_h, v_h);
+    ret += history_sz;
+
+    //measure_txn_counters(txn, "txn_payment");
+    if (likely(txn.commit()))
+      return txn_result(true, ret);
+  } catch (typename Database::abort_exception_type &e) {
+    txn.abort();
+  }
+  return txn_result(false, 0);
+}
+
+template <typename Database>
+class order_line_nop_callback : public
+  Database::template IndexType<schema<order_line>>::type::bytes_search_range_callback {
+
+public:
+  order_line_nop_callback() : n(0) {}
+  virtual bool invoke(
+      const string &key,
+      const string &value)
+  {
+    INVARIANT(key.size() == sizeof(order_line::key));
+    order_line::value v_ol_temp;
+    const order_line::value *v_ol UNUSED = Decode(value, v_ol_temp);
+#ifdef CHECK_INVARIANTS
+    order_line::key k_ol_temp;
+    const order_line::key *k_ol = Decode(key, k_ol_temp);
+    checker::SanityCheckOrderLine(k_ol, v_ol);
+#endif
+    ++n;
+    return true;
+  }
+  size_t n;
+};
+
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, order_status_probe0_tod, order_status_probe0_cg)
+
+template <typename Database, bool AllowReadOnlyScans>
+typename tpcc_worker<Database, AllowReadOnlyScans>::txn_result
+tpcc_worker<Database, AllowReadOnlyScans>::txn_order_status()
+{
+  const uint warehouse_id = PickWarehouseId(this->r, warehouse_id_start, warehouse_id_end);
+  const uint districtID = RandomNumber(this->r, 1, NumDistrictsPerWarehouse());
+
+  // output from txn counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 13
+  //   max_read_set_size : 81
+  //   max_write_set_size : 0
+  //   num_txn_contexts : 4
+  const uint64_t read_only_mask =
+    !AllowReadOnlyScans ? 0 : transaction_base::TXN_FLAG_READ_ONLY;
+  typename Database::template TransactionType<
+    AllowReadOnlyScans ?
+      abstract_db::HINT_TPCC_ORDER_STATUS_READ_ONLY :
+      abstract_db::HINT_TPCC_ORDER_STATUS>::type txn(
+          txn_flags | read_only_mask, this->arena);
+  scoped_str_arena s_arena(this->arena);
+  // NB: since txn_order_status() is a RO txn, we assume that
+  // locking is un-necessary (since we can just read from some old snapshot)
+  try {
+
+    customer::key k_c;
+    customer::value v_c;
+    if (RandomNumber(this->r, 1, 100) <= 60) {
+      // cust by name
+      uint8_t lastname_buf[CustomerLastNameMaxSize + 1];
+      static_assert(sizeof(lastname_buf) == 16, "xx");
+      NDB_MEMSET(lastname_buf, 0, sizeof(lastname_buf));
+      GetNonUniformCustomerLastNameRun(lastname_buf, r);
+
+      static const string zeros(16, 0);
+      static const string ones(16, 255);
+
+      customer_name_idx::key k_c_idx_0;
+      k_c_idx_0.c_w_id = warehouse_id;
+      k_c_idx_0.c_d_id = districtID;
+      k_c_idx_0.c_last.assign((const char *) lastname_buf, 16);
+      k_c_idx_0.c_first.assign(zeros);
+
+      customer_name_idx::key k_c_idx_1;
+      k_c_idx_1.c_w_id = warehouse_id;
+      k_c_idx_1.c_d_id = districtID;
+      k_c_idx_1.c_last.assign((const char *) lastname_buf, 16);
+      k_c_idx_1.c_first.assign(ones);
+
+      // NMaxCustomerIdxScanElems is probably a safe bet for now
+      bytes_static_limit_callback<
+        typename Database::template IndexType<schema<customer_name_idx>>::type,
+        NMaxCustomerIdxScanElems, true> c(s_arena.get());
+      tables.tbl_customer_name_idx(warehouse_id)->bytes_search_range_call(
+          txn, k_c_idx_0, &k_c_idx_1, c);
+      ALWAYS_ASSERT(c.size() > 0);
+      INVARIANT(c.size() < NMaxCustomerIdxScanElems); // we should detect this
+      int index = c.size() / 2;
+      if (c.size() % 2 == 0)
+        index--;
+      evt_avg_cust_name_idx_scan_size.offer(c.size());
+
+      customer_name_idx::value v_c_idx_temp;
+      const customer_name_idx::value *v_c_idx = Decode(c.value(index), v_c_idx_temp);
+
+      k_c.c_w_id = warehouse_id;
+      k_c.c_d_id = districtID;
+      k_c.c_id = v_c_idx->c_id;
+      ALWAYS_ASSERT(tables.tbl_customer(warehouse_id)->search(txn, k_c, v_c));
+
+    } else {
+      // cust by ID
+      const uint customerID = GetCustomerId(r);
+      k_c.c_w_id = warehouse_id;
+      k_c.c_d_id = districtID;
+      k_c.c_id = customerID;
+      ALWAYS_ASSERT(tables.tbl_customer(warehouse_id)->search(txn, k_c, v_c));
+    }
+    checker::SanityCheckCustomer(&k_c, &v_c);
+
+    // XXX(stephentu): HACK- we bound the # of elems returned by this scan to
+    // 15- this is because we don't have reverse scans. In an ideal system, a
+    // reverse scan would only need to read 1 btree node. We could simulate a
+    // lookup by only reading the first element- but then we would *always*
+    // read the first order by any customer.  To make this more interesting, we
+    // randomly select which elem to pick within the 1st or 2nd btree nodes.
+    // This is obviously a deviation from TPC-C, but it shouldn't make that
+    // much of a difference in terms of performance numbers (in fact we are
+    // making it worse for us)
+    latest_key_callback<
+      typename Database::template IndexType<schema<oorder_c_id_idx>>::type> c_oorder(
+          *this->arena.next(), (r.next() % 15) + 1);
+    const oorder_c_id_idx::key k_oo_idx_0(warehouse_id, districtID, k_c.c_id, 0);
+    const oorder_c_id_idx::key k_oo_idx_1(warehouse_id, districtID, k_c.c_id, numeric_limits<int32_t>::max());
+    {
+      ANON_REGION("OrderStatusOOrderScan:", &order_status_probe0_cg);
+      tables.tbl_oorder_c_id_idx(warehouse_id)->bytes_search_range_call(
+          txn, k_oo_idx_0, &k_oo_idx_1, c_oorder);
+    }
+    ALWAYS_ASSERT(c_oorder.size());
+
+    oorder_c_id_idx::key k_oo_idx_temp;
+    const oorder_c_id_idx::key *k_oo_idx = Decode(c_oorder.kstr(), k_oo_idx_temp);
+    const uint o_id = k_oo_idx->o_o_id;
+
+    // XXX: fix
+    // XXX(stephentu): what's wrong w/ it?
+    order_line_nop_callback<Database> c_order_line;
+    const order_line::key k_ol_0(warehouse_id, districtID, o_id, 0);
+    const order_line::key k_ol_1(warehouse_id, districtID, o_id, numeric_limits<int32_t>::max());
+    tables.tbl_order_line(warehouse_id)->bytes_search_range_call(
+        txn, k_ol_0, &k_ol_1, c_order_line);
+    ALWAYS_ASSERT(c_order_line.n >= 5 && c_order_line.n <= 15);
+
+    //measure_txn_counters(txn, "txn_order_status");
+    if (likely(txn.commit()))
+      return txn_result(true, 0);
+  } catch (typename Database::abort_exception_type &e) {
+    txn.abort();
+  }
+  return txn_result(false, 0);
+}
+
+template <typename Database>
+class order_line_scan_callback : public
+  Database::template IndexType<schema<order_line>>::type::bytes_search_range_callback {
+public:
+  order_line_scan_callback() : n(0) {}
+  virtual bool invoke(
+      const string &key,
+      const string &value)
+  {
+    INVARIANT(key.size() == sizeof(order_line::key));
+    order_line::value v_ol;
+
+#ifdef DISABLE_FIELD_SELECTION
+    const uint64_t mask = numeric_limits<uint64_t>::max();
+#else
+    const uint64_t mask = compute_fields_mask(0);
+#endif
+
+    typed_txn_btree_<schema<order_line>>::do_record_read(
+        (const uint8_t *) value.data(), value.size(), mask, &v_ol);
+
+#if defined(CHECK_INVARIANTS) && defined(DISABLE_FIELD_SELECTION)
+    order_line::key k_ol_temp;
+    const order_line::key *k_ol = Decode(key, k_ol_temp);
+    checker::SanityCheckOrderLine(k_ol, &v_ol);
+#endif
+
+    s_i_ids[v_ol.ol_i_id] = 1;
+    n++;
+    return true;
+  }
+  size_t n;
+  small_unordered_map<uint, bool, 512> s_i_ids;
+};
+
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, stock_level_probe0_tod, stock_level_probe0_cg)
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, stock_level_probe1_tod, stock_level_probe1_cg)
+STATIC_COUNTER_DECL(scopedperf::tod_ctr, stock_level_probe2_tod, stock_level_probe2_cg)
+
+static event_avg_counter evt_avg_stock_level_loop_join_lookups("stock_level_loop_join_lookups");
+
+template <typename Database, bool AllowReadOnlyScans>
+typename tpcc_worker<Database, AllowReadOnlyScans>::txn_result
+tpcc_worker<Database, AllowReadOnlyScans>::txn_stock_level()
+{
+  const uint warehouse_id = PickWarehouseId(this->r, warehouse_id_start, warehouse_id_end);
+  const uint threshold = RandomNumber(this->r, 10, 20);
+  const uint districtID = RandomNumber(this->r, 1, NumDistrictsPerWarehouse());
+
+  // output from txn counters:
+  //   max_absent_range_set_size : 0
+  //   max_absent_set_size : 0
+  //   max_node_scan_size : 19
+  //   max_read_set_size : 241
+  //   max_write_set_size : 0
+  //   n_node_scan_large_instances : 1
+  //   n_read_set_large_instances : 2
+  //   num_txn_contexts : 3
+  const uint64_t read_only_mask =
+    !AllowReadOnlyScans ? 0 : transaction_base::TXN_FLAG_READ_ONLY;
+  typename Database::template TransactionType<
+    AllowReadOnlyScans ?
+      abstract_db::HINT_TPCC_STOCK_LEVEL_READ_ONLY :
+      abstract_db::HINT_TPCC_STOCK_LEVEL>::type txn(
+          txn_flags | read_only_mask, this->arena);
+  scoped_str_arena s_arena(this->arena);
+  // NB: since txn_stock_level() is a RO txn, we assume that
+  // locking is un-necessary (since we can just read from some old snapshot)
+  try {
+    const district::key k_d(warehouse_id, districtID);
+    district::value v_d;
+    ALWAYS_ASSERT(tables.tbl_district(warehouse_id)->search(txn, k_d, v_d));
+    checker::SanityCheckDistrict(&k_d, &v_d);
+    const uint64_t cur_next_o_id = g_new_order_fast_id_gen ?
+      NewOrderIdHolder(warehouse_id, districtID).load(memory_order_acquire) :
+      v_d.d_next_o_id;
+
+    // manual joins are fun!
+    order_line_scan_callback<Database> c;
+    const int32_t lower = cur_next_o_id >= 20 ? (cur_next_o_id - 20) : 0;
+    const order_line::key k_ol_0(warehouse_id, districtID, lower, 0);
+    const order_line::key k_ol_1(warehouse_id, districtID, cur_next_o_id, 0);
+    {
+      // mask must be kept in sync w/ order_line_scan_callback
+#ifdef DISABLE_FIELD_SELECTION
+      const size_t nfields = order_line::value::NFIELDS;
+#else
+      const size_t nfields = 1;
+#endif
+      ANON_REGION("StockLevelOrderLineScan:", &stock_level_probe0_cg);
+      tables.tbl_order_line(warehouse_id)->bytes_search_range_call(
+          txn, k_ol_0, &k_ol_1, c, nfields);
+    }
+    {
+      small_unordered_map<uint, bool, 512> s_i_ids_distinct;
+      for (auto &p : c.s_i_ids) {
+        ANON_REGION("StockLevelLoopJoinIter:", &stock_level_probe1_cg);
+        const stock::key k_s(warehouse_id, p.first);
+        stock::value v_s;
+        INVARIANT(p.first >= 1 && p.first <= NumItems());
+        {
+          ANON_REGION("StockLevelLoopJoinGet:", &stock_level_probe2_cg);
+          ALWAYS_ASSERT(tables.tbl_stock(warehouse_id)->search(txn, k_s, v_s, GUARDED_FIELDS(stock::value::s_quantity_field)));
+        }
+        if (v_s.s_quantity < int(threshold))
+          s_i_ids_distinct[p.first] = 1;
+      }
+      evt_avg_stock_level_loop_join_lookups.offer(c.s_i_ids.size());
+      // NB(stephentu): s_i_ids_distinct.size() is the computed result of this txn
+    }
+    //measure_txn_counters(txn, "txn_stock_level");
+    if (likely(txn.commit()))
+      return txn_result(true, 0);
+  } catch (typename Database::abort_exception_type &e) {
+    txn.abort();
+  }
+  return txn_result(false, 0);
+}
+
+template <typename T>
+static vector<T>
+unique_filter(const vector<T> &v)
+{
+  set<T> seen;
+  vector<T> ret;
+  for (auto &e : v)
+    if (!seen.count(e)) {
+      ret.emplace_back(e);
+      seen.insert(e);
+    }
+  return ret;
+}
+
+template <typename Database, bool AllowReadOnlyScans>
+class tpcc_bench_runner : public typed_bench_runner<Database> {
+private:
+
+  static bool
+  IsTableReadOnly(const char *name)
+  {
+    return strcmp("item", name) == 0;
+  }
+
+  static bool
+  IsTableAppendOnly(const char *name)
+  {
+    return strcmp("history", name) == 0 ||
+           strcmp("oorder_c_id_idx", name) == 0;
+  }
+
+  template <typename Schema>
+  static vector<shared_ptr<typename Database::template IndexType<Schema>::type>>
+  OpenTablesForTablespace(Database *db, const char *name, size_t expected_size)
+  {
+    const bool is_read_only = IsTableReadOnly(name);
+    const bool is_append_only = IsTableAppendOnly(name);
+    const string s_name(name);
+    vector<typename Database::template IndexType<Schema>::ptr_type> ret(NumWarehouses());
+    if (g_enable_separate_tree_per_partition && !is_read_only) {
+      if (NumWarehouses() <= nthreads) {
+        for (size_t i = 0; i < NumWarehouses(); i++)
+          ret[i] = db->template open_index<Schema>(s_name + "_" + to_string(i), expected_size, is_append_only);
+      } else {
+        const unsigned nwhse_per_partition = NumWarehouses() / nthreads;
+        for (size_t partid = 0; partid < nthreads; partid++) {
+          const unsigned wstart = partid * nwhse_per_partition;
+          const unsigned wend   = (partid + 1 == nthreads) ?
+            NumWarehouses() : (partid + 1) * nwhse_per_partition;
+          auto idx =
+            db->template open_index<Schema>(s_name + "_" + to_string(partid), expected_size, is_append_only);
+          for (size_t i = wstart; i < wend; i++)
+            ret[i] = idx;
+        }
+      }
+    } else {
+      auto idx = db->template open_index<Schema>(s_name, expected_size, is_append_only);
+      for (size_t i = 0; i < NumWarehouses(); i++)
+        ret[i] = idx;
+    }
+    return ret;
+  }
+
+public:
+  tpcc_bench_runner(Database *db)
+    : typed_bench_runner<Database>(db)
+  {
+
+#define OPEN_TABLESPACE_X(x) \
+    do { \
+      tables.tbl_ ## x ## _vec = OpenTablesForTablespace<schema<x>>(db, #x, sizeof(x::value)); \
+      auto v = unique_filter(tables.tbl_ ## x ## _vec); \
+      for (size_t i = 0; i < v.size(); i++) \
+        this->open_tables[string(#x) + "_" + to_string(i)] = v[i]; \
+    } while (0);
+
+    TPCC_TABLE_LIST(OPEN_TABLESPACE_X);
+
+#undef OPEN_TABLESPACE_X
+
+    if (g_enable_partition_locks) {
+      static_assert(sizeof(aligned_padded_elem<spinlock>) == CACHELINE_SIZE, "xx");
+      void * const px = memalign(CACHELINE_SIZE, sizeof(aligned_padded_elem<spinlock>) * nthreads);
+      ALWAYS_ASSERT(px);
+      ALWAYS_ASSERT(reinterpret_cast<uintptr_t>(px) % CACHELINE_SIZE == 0);
+      g_partition_locks = reinterpret_cast<aligned_padded_elem<spinlock> *>(px);
+      for (size_t i = 0; i < nthreads; i++) {
+        new (&g_partition_locks[i]) aligned_padded_elem<spinlock>();
+        ALWAYS_ASSERT(!g_partition_locks[i].elem.is_locked());
+      }
+    }
+
+    if (g_new_order_fast_id_gen) {
+      void * const px =
+        memalign(
+            CACHELINE_SIZE,
+            sizeof(aligned_padded_elem<atomic<uint64_t>>) *
+              NumWarehouses() * NumDistrictsPerWarehouse());
+      g_district_ids = reinterpret_cast<aligned_padded_elem<atomic<uint64_t>> *>(px);
+      for (size_t i = 0; i < NumWarehouses() * NumDistrictsPerWarehouse(); i++)
+        new (&g_district_ids[i]) atomic<uint64_t>(3001);
+    }
+  }
+
+protected:
+  virtual vector<unique_ptr<bench_loader>>
+  make_loaders()
+  {
+    vector<unique_ptr<bench_loader>> ret;
+    ret.emplace_back(new tpcc_warehouse_loader<Database>(9324, this->typed_db(), tables));
+    ret.emplace_back(new tpcc_item_loader<Database>(235443, this->typed_db(), tables));
+    if (enable_parallel_loading) {
+      fast_random r(89785943);
+      for (uint i = 1; i <= NumWarehouses(); i++)
+        ret.emplace_back(new tpcc_stock_loader<Database>(r.next(), this->typed_db(), tables, i));
+    } else {
+      ret.emplace_back(new tpcc_stock_loader<Database>(89785943, this->typed_db(), tables, -1));
+    }
+    ret.emplace_back(new tpcc_district_loader<Database>(129856349, this->typed_db(), tables));
+    if (enable_parallel_loading) {
+      fast_random r(923587856425);
+      for (uint i = 1; i <= NumWarehouses(); i++)
+        ret.emplace_back(new tpcc_customer_loader<Database>(r.next(), this->typed_db(), tables, i));
+    } else {
+      ret.emplace_back(new tpcc_customer_loader<Database>(923587856425, this->typed_db(), tables, -1));
+    }
+    if (enable_parallel_loading) {
+      fast_random r(2343352);
+      for (uint i = 1; i <= NumWarehouses(); i++)
+        ret.emplace_back(new tpcc_order_loader<Database>(r.next(), this->typed_db(), tables, i));
+    } else {
+      ret.emplace_back(new tpcc_order_loader<Database>(2343352, this->typed_db(), tables, -1));
+    }
+    return ret;
+  }
+
+private:
+  template <bool RO>
+  vector<unique_ptr<bench_worker>>
+  make_workers_impl()
+  {
+    const unsigned alignment = coreid::num_cpus_online();
+    const int blockstart =
+      coreid::allocate_contiguous_aligned_block(nthreads, alignment);
+    ALWAYS_ASSERT(blockstart >= 0);
+    ALWAYS_ASSERT((blockstart % alignment) == 0);
+    fast_random r(23984543);
+    vector<unique_ptr<bench_worker>> ret;
+    if (NumWarehouses() <= nthreads) {
+      for (size_t i = 0; i < nthreads; i++)
+        ret.emplace_back(
+          new tpcc_worker<Database, RO>(
+            blockstart + i,
+            r.next(), this->typed_db(), tables,
+            &this->barrier_a, &this->barrier_b,
+            (i % NumWarehouses()) + 1, (i % NumWarehouses()) + 2));
+    } else {
+      const unsigned nwhse_per_partition = NumWarehouses() / nthreads;
+      for (size_t i = 0; i < nthreads; i++) {
+        const unsigned wstart = i * nwhse_per_partition;
+        const unsigned wend   = (i + 1 == nthreads) ?
+          NumWarehouses() : (i + 1) * nwhse_per_partition;
+        ret.emplace_back(
+          new tpcc_worker<Database, RO>(
+            blockstart + i,
+            r.next(), this->typed_db(), tables,
+            &this->barrier_a, &this->barrier_b, wstart+1, wend+2));
+      }
+    }
+    return ret;
+  }
+
+protected:
+  virtual vector<unique_ptr<bench_worker>>
+  make_workers()
+  {
+    return g_disable_read_only_scans ? make_workers_impl<false>() : make_workers_impl<true>();
+  }
+
+private:
+  tpcc_tables<Database> tables;
+};
+
+template <typename Database>
+static unique_ptr<bench_runner>
+MakeBenchRunner(Database *db)
+{
+  return unique_ptr<bench_runner>(
+    g_disable_read_only_scans ?
+      static_cast<bench_runner *>(new tpcc_bench_runner<Database, false>(db)) :
+      static_cast<bench_runner *>(new tpcc_bench_runner<Database, true>(db)));
+}
+
+void
+tpcc_do_test(const string &dbtype,
+             const persistconfig &cfg,
+             int argc, char **argv)
+{
+  // parse options
+  optind = 1;
+  bool did_spec_remote_pct = false;
+  while (1) {
+    static struct option long_options[] =
+    {
+      {"disable-cross-partition-transactions" , no_argument       , &g_disable_xpartition_txn             , 1}   ,
+      {"disable-read-only-snapshots"          , no_argument       , &g_disable_read_only_scans            , 1}   ,
+      {"enable-partition-locks"               , no_argument       , &g_enable_partition_locks             , 1}   ,
+      {"enable-separate-tree-per-partition"   , no_argument       , &g_enable_separate_tree_per_partition , 1}   ,
+      {"new-order-remote-item-pct"            , required_argument , 0                                     , 'r'} ,
+      {"new-order-fast-id-gen"                , no_argument       , &g_new_order_fast_id_gen              , 1}   ,
+      {"uniform-item-dist"                    , no_argument       , &g_uniform_item_dist                  , 1}   ,
+      {"workload-mix"                         , required_argument , 0                                     , 'w'} ,
+      {0, 0, 0, 0}
+    };
+    int option_index = 0;
+    int c = getopt_long(argc, argv, "r:", long_options, &option_index);
+    if (c == -1)
+      break;
+    switch (c) {
+    case 0:
+      if (long_options[option_index].flag != 0)
+        break;
+      abort();
+      break;
+
+    case 'r':
+      g_new_order_remote_item_pct = strtoul(optarg, NULL, 10);
+      ALWAYS_ASSERT(g_new_order_remote_item_pct >= 0 && g_new_order_remote_item_pct <= 100);
+      did_spec_remote_pct = true;
+      break;
+
+    case 'w':
+      {
+        const vector<string> toks = split(optarg, ',');
+        ALWAYS_ASSERT(toks.size() == ARRAY_NELEMS(g_txn_workload_mix));
+        unsigned s = 0;
+        for (size_t i = 0; i < toks.size(); i++) {
+          unsigned p = strtoul(toks[i].c_str(), nullptr, 10);
+          ALWAYS_ASSERT(p >= 0 && p <= 100);
+          s += p;
+          g_txn_workload_mix[i] = p;
+        }
+        ALWAYS_ASSERT(s == 100);
+      }
+      break;
+
+    case '?':
+      /* getopt_long already printed an error message. */
+      exit(1);
+
+    default:
+      abort();
+    }
+  }
+
+  if (did_spec_remote_pct && g_disable_xpartition_txn) {
+    cerr << "WARNING: --new-order-remote-item-pct given with --disable-cross-partition-transactions" << endl;
+    cerr << "  --new-order-remote-item-pct will have no effect" << endl;
+  }
+
+  if (verbose) {
+    cerr << "tpcc settings:" << endl;
+    cerr << "  cross_partition_transactions : " << !g_disable_xpartition_txn << endl;
+    cerr << "  read_only_snapshots          : " << !g_disable_read_only_scans << endl;
+    cerr << "  partition_locks              : " << g_enable_partition_locks << endl;
+    cerr << "  separate_tree_per_partition  : " << g_enable_separate_tree_per_partition << endl;
+    cerr << "  new_order_remote_item_pct    : " << g_new_order_remote_item_pct << endl;
+    cerr << "  new_order_fast_id_gen        : " << g_new_order_fast_id_gen << endl;
+    cerr << "  uniform_item_dist            : " << g_uniform_item_dist << endl;
+    cerr << "  workload_mix                 : " <<
+      format_list(g_txn_workload_mix,
+                  g_txn_workload_mix + ARRAY_NELEMS(g_txn_workload_mix)) << endl;
+  }
+
+  unique_ptr<abstract_db> db;
+  unique_ptr<bench_runner> r;
+
+  if (dbtype == "ndb-proto2") {
+    if (!cfg.logfiles_.empty()) {
+      vector<vector<unsigned>> assignments_used;
+      txn_logger::Init(
+          nthreads, cfg.logfiles_, cfg.assignments_, &assignments_used,
+          !cfg.nofsync_,
+          cfg.do_compress_,
+          cfg.fake_writes_);
+      if (verbose) {
+        cerr << "[logging subsystem]" << endl;
+        cerr << "  assignments: " << assignments_used  << endl;
+        cerr << "  call fsync : " << !cfg.nofsync_     << endl;
+        cerr << "  compression: " << cfg.do_compress_  << endl;
+        cerr << "  fake_writes: " << cfg.fake_writes_  << endl;
+      }
+    }
+#ifdef PROTO2_CAN_DISABLE_GC
+    if (!cfg.disable_gc_)
+      transaction_proto2_static::InitGC();
+#endif
+#ifdef PROTO2_CAN_DISABLE_SNAPSHOTS
+    if (cfg.disable_snapshots_)
+      transaction_proto2_static::DisableSnapshots();
+#endif
+    typedef ndb_database<transaction_proto2> Database;
+    Database *raw = new Database;
+    db.reset(raw);
+    r = MakeBenchRunner(raw);
+  } else if (dbtype == "kvdb-st") {
+    typedef kvdb_database<false> Database;
+    Database *raw = new Database;
+    db.reset(raw);
+    r = MakeBenchRunner(raw);
+  } else
+    ALWAYS_ASSERT(false);
+
+  r->run();
+}
diff --git a/silo/new-benchmarks/tpcc.h b/silo/new-benchmarks/tpcc.h
new file mode 100644 (file)
index 0000000..7f76cb3
--- /dev/null
@@ -0,0 +1,208 @@
+#ifndef _NDB_BENCH_TPCC_H_
+#define _NDB_BENCH_TPCC_H_
+
+#include <memory>
+
+#include "../record/encoder.h"
+#include "../record/inline_str.h"
+#include "../macros.h"
+
+#define CUSTOMER_KEY_FIELDS(x, y) \
+  x(int32_t,c_w_id) \
+  y(int32_t,c_d_id) \
+  y(int32_t,c_id)
+#define CUSTOMER_VALUE_FIELDS(x, y) \
+  x(float,c_discount) \
+  y(inline_str_fixed<2>,c_credit) \
+  y(inline_str_8<16>,c_last) \
+  y(inline_str_8<16>,c_first) \
+  y(float,c_credit_lim) \
+  y(float,c_balance) \
+  y(float,c_ytd_payment) \
+  y(int32_t,c_payment_cnt) \
+  y(int32_t,c_delivery_cnt) \
+  y(inline_str_8<20>,c_street_1) \
+  y(inline_str_8<20>,c_street_2) \
+  y(inline_str_8<20>,c_city) \
+  y(inline_str_fixed<2>,c_state) \
+  y(inline_str_fixed<9>,c_zip) \
+  y(inline_str_fixed<16>,c_phone) \
+  y(uint32_t,c_since) \
+  y(inline_str_fixed<2>,c_middle) \
+  y(inline_str_16<500>,c_data)
+DO_STRUCT(customer, CUSTOMER_KEY_FIELDS, CUSTOMER_VALUE_FIELDS)
+
+#define CUSTOMER_NAME_IDX_KEY_FIELDS(x, y) \
+  x(int32_t,c_w_id) \
+  y(int32_t,c_d_id) \
+  y(inline_str_fixed<16>,c_last) \
+  y(inline_str_fixed<16>,c_first)
+#define CUSTOMER_NAME_IDX_VALUE_FIELDS(x, y) \
+       x(int32_t,c_id)
+DO_STRUCT(customer_name_idx, CUSTOMER_NAME_IDX_KEY_FIELDS, CUSTOMER_NAME_IDX_VALUE_FIELDS)
+
+#define DISTRICT_KEY_FIELDS(x, y) \
+  x(int32_t,d_w_id) \
+  y(int32_t,d_id)
+#define DISTRICT_VALUE_FIELDS(x, y) \
+  x(float,d_ytd) \
+  y(float,d_tax) \
+  y(int32_t,d_next_o_id) \
+  y(inline_str_8<10>,d_name) \
+  y(inline_str_8<20>,d_street_1) \
+  y(inline_str_8<20>,d_street_2) \
+  y(inline_str_8<20>,d_city) \
+  y(inline_str_fixed<2>,d_state) \
+  y(inline_str_fixed<9>,d_zip)
+DO_STRUCT(district, DISTRICT_KEY_FIELDS, DISTRICT_VALUE_FIELDS)
+
+#define HISTORY_KEY_FIELDS(x, y) \
+  x(int32_t,h_c_id) \
+  y(int32_t,h_c_d_id) \
+  y(int32_t,h_c_w_id) \
+  y(int32_t,h_d_id) \
+  y(int32_t,h_w_id) \
+  y(uint32_t,h_date)
+#define HISTORY_VALUE_FIELDS(x, y) \
+  x(float,h_amount) \
+  y(inline_str_8<24>,h_data)
+DO_STRUCT(history, HISTORY_KEY_FIELDS, HISTORY_VALUE_FIELDS)
+
+#define ITEM_KEY_FIELDS(x, y) \
+  x(int32_t,i_id)
+#define ITEM_VALUE_FIELDS(x, y) \
+  x(inline_str_8<24>,i_name) \
+  y(float,i_price) \
+  y(inline_str_8<50>,i_data) \
+  y(int32_t,i_im_id)
+DO_STRUCT(item, ITEM_KEY_FIELDS, ITEM_VALUE_FIELDS)
+
+#define NEW_ORDER_KEY_FIELDS(x, y) \
+  x(int32_t,no_w_id) \
+  y(int32_t,no_d_id) \
+  y(int32_t,no_o_id)
+// need dummy b/c our btree cannot have empty values.
+// we also size value so that it can fit a key
+#define NEW_ORDER_VALUE_FIELDS(x, y) \
+  x(inline_str_fixed<12>,no_dummy)
+DO_STRUCT(new_order, NEW_ORDER_KEY_FIELDS, NEW_ORDER_VALUE_FIELDS)
+
+#define OORDER_KEY_FIELDS(x, y) \
+  x(int32_t,o_w_id) \
+  y(int32_t,o_d_id) \
+  y(int32_t,o_id)
+#define OORDER_VALUE_FIELDS(x, y) \
+  x(int32_t,o_c_id) \
+  y(int32_t,o_carrier_id) \
+  y(int8_t,o_ol_cnt) \
+  y(bool,o_all_local) \
+  y(uint32_t,o_entry_d)
+DO_STRUCT(oorder, OORDER_KEY_FIELDS, OORDER_VALUE_FIELDS)
+
+#define OORDER_C_ID_IDX_KEY_FIELDS(x, y) \
+  x(int32_t,o_w_id) \
+  y(int32_t,o_d_id) \
+  y(int32_t,o_c_id) \
+  y(int32_t,o_o_id)
+#define OORDER_C_ID_IDX_VALUE_FIELDS(x, y) \
+       x(uint8_t,o_dummy)
+DO_STRUCT(oorder_c_id_idx, OORDER_C_ID_IDX_KEY_FIELDS, OORDER_C_ID_IDX_VALUE_FIELDS)
+
+#define ORDER_LINE_KEY_FIELDS(x, y) \
+  x(int32_t,ol_w_id) \
+  y(int32_t,ol_d_id) \
+  y(int32_t,ol_o_id) \
+  y(int32_t,ol_number)
+#define ORDER_LINE_VALUE_FIELDS(x, y) \
+  x(int32_t,ol_i_id) \
+  y(uint32_t,ol_delivery_d) \
+  y(float,ol_amount) \
+  y(int32_t,ol_supply_w_id) \
+  y(int8_t,ol_quantity)
+DO_STRUCT(order_line, ORDER_LINE_KEY_FIELDS, ORDER_LINE_VALUE_FIELDS)
+
+#define STOCK_KEY_FIELDS(x, y) \
+  x(int32_t,s_w_id) \
+  y(int32_t,s_i_id)
+#define STOCK_VALUE_FIELDS(x, y) \
+  x(int16_t,s_quantity) \
+  y(float,s_ytd) \
+  y(int32_t,s_order_cnt) \
+  y(int32_t,s_remote_cnt)
+DO_STRUCT(stock, STOCK_KEY_FIELDS, STOCK_VALUE_FIELDS)
+
+#define STOCK_DATA_KEY_FIELDS(x, y) \
+  x(int32_t,s_w_id) \
+  y(int32_t,s_i_id)
+#define STOCK_DATA_VALUE_FIELDS(x, y) \
+  x(inline_str_8<50>,s_data) \
+  y(inline_str_fixed<24>,s_dist_01) \
+  y(inline_str_fixed<24>,s_dist_02) \
+  y(inline_str_fixed<24>,s_dist_03) \
+  y(inline_str_fixed<24>,s_dist_04) \
+  y(inline_str_fixed<24>,s_dist_05) \
+  y(inline_str_fixed<24>,s_dist_06) \
+  y(inline_str_fixed<24>,s_dist_07) \
+  y(inline_str_fixed<24>,s_dist_08) \
+  y(inline_str_fixed<24>,s_dist_09) \
+  y(inline_str_fixed<24>,s_dist_10)
+DO_STRUCT(stock_data, STOCK_DATA_KEY_FIELDS, STOCK_DATA_VALUE_FIELDS)
+
+#define WAREHOUSE_KEY_FIELDS(x, y) \
+  x(int32_t,w_id)
+#define WAREHOUSE_VALUE_FIELDS(x, y) \
+  x(float,w_ytd) \
+  y(float,w_tax) \
+  y(inline_str_8<10>,w_name) \
+  y(inline_str_8<20>,w_street_1) \
+  y(inline_str_8<20>,w_street_2) \
+  y(inline_str_8<20>,w_city) \
+  y(inline_str_fixed<2>,w_state) \
+  y(inline_str_fixed<9>,w_zip)
+DO_STRUCT(warehouse, WAREHOUSE_KEY_FIELDS, WAREHOUSE_VALUE_FIELDS)
+
+#define TPCC_TABLE_LIST(x) \
+  x(customer) \
+  x(customer_name_idx) \
+  x(district) \
+  x(history) \
+  x(item) \
+  x(new_order) \
+  x(oorder) \
+  x(oorder_c_id_idx) \
+  x(order_line) \
+  x(stock) \
+  x(stock_data) \
+  x(warehouse)
+
+template <typename Database, bool AllowReadOnlyScans>
+  class tpcc_bench_runner;
+
+template <typename Database>
+struct tpcc_tables {
+  template <typename D, bool B>
+    friend class tpcc_bench_runner;
+
+#define DEFN_TBL_ACCESSOR_X(name) \
+private:  \
+  std::vector< \
+    std::shared_ptr< \
+      typename Database::template IndexType< \
+        schema< name > \
+      >::type \
+    > \
+  > tbl_ ## name ## _vec; \
+public: \
+  inline ALWAYS_INLINE typename Database::template IndexType<schema< name >>::type * \
+  tbl_ ## name(unsigned int wid) \
+  { \
+    INVARIANT(wid >= 1); \
+    return (tbl_ ## name ## _vec[wid - 1]).get(); \
+  }
+
+  TPCC_TABLE_LIST(DEFN_TBL_ACCESSOR_X)
+
+#undef DEFN_TBL_ACCESSOR_X
+};
+
+#endif
diff --git a/silo/ownership_checker.h b/silo/ownership_checker.h
new file mode 100644 (file)
index 0000000..4d6cbdb
--- /dev/null
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <vector>
+#include "macros.h"
+
+/**
+ * T - node type. needs to implement is_lock_owner()
+ */
+template <typename Scope, typename T>
+class ownership_checker {
+public:
+  static void
+  NodeLockRegionBegin()
+  {
+    MyLockedNodes(true)->clear();
+  }
+
+  // is used to signal the end of a tuple lock region
+  static void
+  AssertAllNodeLocksReleased()
+  {
+    std::vector<const T *> *nodes = MyLockedNodes(false);
+    ALWAYS_ASSERT(nodes);
+    for (auto p : *nodes)
+      ALWAYS_ASSERT(!p->is_lock_owner());
+    nodes->clear();
+  }
+
+  static void
+  AddNodeToLockRegion(const T *n)
+  {
+    ALWAYS_ASSERT(n->is_locked());
+    ALWAYS_ASSERT(n->is_lock_owner());
+    std::vector<const T *> *nodes = MyLockedNodes(false);
+    if (nodes)
+      nodes->push_back(n);
+  }
+
+private:
+  static std::vector<const T *> *
+  MyLockedNodes(bool create)
+  {
+    static __thread std::vector<const T *> *tl_locked_nodes = nullptr;
+    if (unlikely(!tl_locked_nodes) && create)
+      tl_locked_nodes = new std::vector<const T *>;
+    return tl_locked_nodes;
+  }
+};
diff --git a/silo/persist_test.cc b/silo/persist_test.cc
new file mode 100644 (file)
index 0000000..604a739
--- /dev/null
@@ -0,0 +1,1221 @@
+/**
+ * A stand-alone binary which doesn't depend on the system,
+ * used to test the current persistence strategy
+ */
+
+#include <cassert>
+#include <iostream>
+#include <cstdint>
+#include <random>
+#include <vector>
+#include <set>
+#include <atomic>
+#include <thread>
+#include <sstream>
+
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <time.h>
+
+#include <lz4.h>
+
+#include "macros.h"
+#include "circbuf.h"
+#include "amd64.h"
+#include "record/serializer.h"
+#include "util.h"
+
+using namespace std;
+using namespace util;
+
+struct tidhelpers {
+  // copied from txn_proto2_impl.h
+
+  static const uint64_t NBitsNumber = 24;
+
+  static const size_t CoreBits = NMAXCOREBITS; // allow 2^CoreShift distinct threads
+  static const size_t NMaxCores = NMAXCORES;
+
+  static const uint64_t CoreMask = (NMaxCores - 1);
+
+  static const uint64_t NumIdShift = CoreBits;
+  static const uint64_t NumIdMask = ((((uint64_t)1) << NBitsNumber) - 1) << NumIdShift;
+
+  static const uint64_t EpochShift = CoreBits + NBitsNumber;
+  static const uint64_t EpochMask = ((uint64_t)-1) << EpochShift;
+
+  static inline
+  uint64_t CoreId(uint64_t v)
+  {
+    return v & CoreMask;
+  }
+
+  static inline
+  uint64_t NumId(uint64_t v)
+  {
+    return (v & NumIdMask) >> NumIdShift;
+  }
+
+  static inline
+  uint64_t EpochId(uint64_t v)
+  {
+    return (v & EpochMask) >> EpochShift;
+  }
+
+  static inline
+  uint64_t MakeTid(uint64_t core_id, uint64_t num_id, uint64_t epoch_id)
+  {
+    // some sanity checking
+    static_assert((CoreMask | NumIdMask | EpochMask) == ((uint64_t)-1), "xx");
+    static_assert((CoreMask & NumIdMask) == 0, "xx");
+    static_assert((NumIdMask & EpochMask) == 0, "xx");
+    return (core_id) | (num_id << NumIdShift) | (epoch_id << EpochShift);
+  }
+
+  static uint64_t
+  vecidmax(uint64_t coremax, const vector<uint64_t> &v)
+  {
+    uint64_t ret = NumId(coremax);
+    for (size_t i = 0; i < v.size(); i++)
+      ret = max(ret, NumId(v[i]));
+    return ret;
+  }
+
+  static string
+  Str(uint64_t v)
+  {
+    ostringstream b;
+    b << "[core=" << CoreId(v) << " | n="
+      << NumId(v) << " | epoch="
+      << EpochId(v) << "]";
+    return b.str();
+  }
+
+};
+
+//static void
+//fillstring(std::string &s, size_t t)
+//{
+//  s.clear();
+//  for (size_t i = 0; i < t; i++)
+//    s[i] = (char) i;
+//}
+
+template <typename PRNG>
+static inline void
+fillkey(std::string &s, uint64_t idx, size_t sz, PRNG &prng)
+{
+  s.resize(sz);
+  serializer<uint64_t, false> ser;
+  ser.write((uint8_t *) s.data(), idx);
+}
+
+template <typename PRNG>
+static inline void
+fillvalue(std::string &s, uint64_t idx, size_t sz, PRNG &prng)
+{
+  uniform_int_distribution<uint32_t> dist(0, 10000);
+  s.resize(sz);
+  serializer<uint32_t, false> s_uint32_t;
+  for (size_t i = 0; i < sz; i += sizeof(uint32_t)) {
+    if (i + sizeof(uint32_t) <= sz) {
+      const uint32_t x = dist(prng);
+      s_uint32_t.write((uint8_t *) &s[i], x);
+    }
+  }
+}
+
+/** simulate global database state */
+
+static const size_t g_nrecords = 1000000;
+static const size_t g_ntxns_worker = 1000000;
+static const size_t g_nmax_loggers = 16;
+
+static vector<uint64_t> g_database;
+static atomic<uint64_t> g_ntxns_committed(0);
+static atomic<uint64_t> g_ntxns_written(0);
+static atomic<uint64_t> g_bytes_written[g_nmax_loggers];
+
+static size_t g_nworkers = 1;
+static int g_verbose = 0;
+static int g_fsync_background = 0;
+static size_t g_readset = 30;
+static size_t g_writeset = 16;
+static size_t g_keysize = 8; // in bytes
+static size_t g_valuesize = 32; // in bytes
+
+/** simulation framework */
+
+// all simulations are epoch based
+class database_simulation {
+public:
+  static const unsigned long g_epoch_time_ns = 30000000; /* 30ms in ns */
+
+  database_simulation()
+    : keep_going_(true),
+      epoch_thread_(),
+      epoch_number_(1), // start at 1 so 0 can be fully persistent initially
+      system_sync_epoch_(0)
+  {
+    // XXX: depends on g_nworkers to be set by now
+    for (size_t i = 0; i < g_nworkers; i++)
+      per_thread_epochs_[i]->store(1, memory_order_release);
+    for (size_t i = 0; i < g_nmax_loggers; i++)
+      for (size_t j = 0; j < g_nworkers; j++)
+        per_thread_sync_epochs_[i].epochs_[j].store(0, memory_order_release);
+  }
+
+  virtual ~database_simulation() {}
+
+  virtual void
+  init()
+  {
+    epoch_thread_ = move(thread(&database_simulation::epoch_thread, this));
+  }
+
+  virtual void worker(unsigned id) = 0;
+
+  virtual void logger(const vector<int> &fd,
+                      const vector<vector<unsigned>> &assignments) = 0;
+
+  virtual void
+  terminate()
+  {
+    keep_going_->store(false, memory_order_release);
+    epoch_thread_.join();
+  }
+
+  static bool
+  AssignmentsValid(const vector<vector<unsigned>> &assignments,
+                   unsigned nfds,
+                   unsigned nworkers)
+  {
+    // each worker must be assigned exactly once in the assignment
+    // there must be <= nfds assignments
+
+    if (assignments.size() > nfds)
+      return false;
+
+    set<unsigned> seen;
+    for (auto &assignment : assignments)
+      for (auto w : assignment) {
+        if (seen.count(w) || w >= nworkers)
+          return false;
+        seen.insert(w);
+      }
+
+    return seen.size() == nworkers;
+  }
+
+protected:
+  void
+  epoch_thread()
+  {
+    while (keep_going_->load(memory_order_acquire)) {
+      struct timespec t;
+      t.tv_sec  = g_epoch_time_ns / ONE_SECOND_NS;
+      t.tv_nsec = g_epoch_time_ns % ONE_SECOND_NS;
+      nanosleep(&t, nullptr);
+
+      // make sure all threads are at the current epoch
+      const uint64_t curepoch = epoch_number_->load(memory_order_acquire);
+
+    retry:
+      bool allthere = true;
+      for (size_t i = 0;
+           i < g_nworkers && keep_going_->load(memory_order_acquire);
+           i++) {
+        if (per_thread_epochs_[i]->load(memory_order_acquire) < curepoch) {
+          allthere = false;
+          break;
+        }
+      }
+      if (!keep_going_->load(memory_order_acquire))
+        return;
+      if (!allthere) {
+        nop_pause();
+        goto retry;
+      }
+
+      //cerr << "bumping epoch" << endl;
+      epoch_number_->store(curepoch + 1, memory_order_release); // bump it
+    }
+  }
+
+  aligned_padded_elem<atomic<bool>> keep_going_;
+
+  thread epoch_thread_;
+
+  aligned_padded_elem<atomic<uint64_t>> epoch_number_;
+
+  aligned_padded_elem<atomic<uint64_t>> per_thread_epochs_[NMAXCORES];
+
+  // v = per_thread_sync_epochs_[i].epochs_[j]: logger i has persisted up
+  // through (including) all transactions <= epoch v on core j. since core =>
+  // logger mapping is static, taking:
+  //   min_{core} max_{logger} per_thread_sync_epochs_[logger].epochs_[core]
+  // yields the entire system's persistent epoch
+  struct {
+    atomic<uint64_t> epochs_[NMAXCORES];
+    CACHE_PADOUT;
+  } per_thread_sync_epochs_[g_nmax_loggers] CACHE_ALIGNED;
+
+  // conservative estimate (<=) for:
+  //   min_{core} max_{logger} per_thread_sync_epochs_[logger].epochs_[core]
+  aligned_padded_elem<atomic<uint64_t>> system_sync_epoch_;
+};
+
+struct logbuf_header {
+  uint64_t nentries_; // > 0 for all valid log buffers
+  uint64_t last_tid_; // TID of the last commit
+} PACKED;
+
+struct pbuffer {
+  bool io_scheduled_; // has the logger scheduled IO yet?
+  size_t curoff_; // current offset into buf_, either for writing
+                  // or during the dep computation phase
+  size_t remaining_; // number of deps remaining to compute
+  std::string buf_; // the actual buffer, of size g_buffer_size
+
+  inline uint8_t *
+  pointer()
+  {
+    return (uint8_t *) buf_.data() + curoff_;
+  }
+
+  inline logbuf_header *
+  header()
+  {
+    return (logbuf_header *) buf_.data();
+  }
+
+  inline const logbuf_header *
+  header() const
+  {
+    return (const logbuf_header *) buf_.data();
+  }
+};
+
+class onecopy_logbased_simulation : public database_simulation {
+public:
+  static const size_t g_perthread_buffers = 64; // 64 outstanding buffers
+  static const size_t g_buffer_size = (1<<20); // in bytes
+  static const size_t g_horizon_size = (1<<16); // in bytes, for compression only
+
+  static circbuf<pbuffer, g_perthread_buffers> g_all_buffers[NMAXCORES];
+  static circbuf<pbuffer, g_perthread_buffers> g_persist_buffers[NMAXCORES];
+
+protected:
+
+  virtual const uint8_t *
+  read_log_entry(const uint8_t *p, uint64_t &tid,
+                 std::function<void(uint64_t)> readfunctor) = 0;
+
+  virtual uint64_t
+  compute_log_record_space() const = 0;
+
+  virtual void
+  write_log_record(uint8_t *p,
+                   uint64_t tidcommit,
+                   const vector<uint64_t> &readset,
+                   const vector<pair<string, string>> &writeset) = 0;
+
+  virtual void
+  logger_on_io_completion() {}
+
+  virtual bool
+  do_compression() const = 0;
+
+  pbuffer *
+  getbuffer(unsigned id)
+  {
+    // block until we get a buf
+    pbuffer *ret = g_all_buffers[id].deq();
+    ret->io_scheduled_ = false;
+    ret->buf_.assign(g_buffer_size, 0);
+    ret->curoff_ = sizeof(logbuf_header);
+    ret->remaining_ = 0;
+    return ret;
+  }
+
+public:
+  void
+  init() OVERRIDE
+  {
+    database_simulation::init();
+    for (size_t i = 0; i < g_nworkers; i++) {
+      for (size_t j = 0; j < g_perthread_buffers; j++) {
+        struct pbuffer *p = new pbuffer;
+        g_all_buffers[i].enq(p);
+      }
+    }
+  }
+
+private:
+  inline size_t
+  inplace_update_persistent_info(
+      vector<pair<uint64_t, uint64_t>> &outstanding_commits,
+      uint64_t cursyncepoch)
+  {
+    size_t ncommits_synced = 0;
+    // can erase all entries with x.first <= cursyncepoch
+    size_t idx = 0;
+    for (; idx < outstanding_commits.size(); idx++) {
+      if (outstanding_commits[idx].first <= cursyncepoch)
+        ncommits_synced += outstanding_commits[idx].second;
+      else
+        break;
+    }
+
+    // erase entries [0, idx)
+    // XXX: slow
+    outstanding_commits.erase(outstanding_commits.begin(),
+        outstanding_commits.begin() + idx);
+
+    return ncommits_synced;
+  }
+
+  inline pbuffer *
+  ensure_buffer_with_space(unsigned id, pbuffer *cur, size_t space_needed)
+  {
+    if (!cur) {
+      cur = getbuffer(id);
+    } else if (g_buffer_size - cur->curoff_ < space_needed) {
+      g_persist_buffers[id].enq(cur);
+      cur = getbuffer(id);
+    }
+    INVARIANT(cur);
+    INVARIANT(g_buffer_size - cur->curoff_ >= space_needed);
+    return cur;
+  }
+
+  /**
+   * write the horizon from [p, p+sz) into cur, assuming that cur has enough
+   * space. space needed is at least:
+   *   sizeof(uint32_t) + LZ4_compressBound(sz)
+   *
+   * also updates the buffer's headers and offset to reflect the write
+   *
+   * returns the compressed size of the horizon
+   */
+  inline uint64_t
+  write_horizon(void *lz4ctx,
+                const uint8_t *p, uint64_t sz,
+                uint64_t nentries, uint64_t lasttid,
+                pbuffer *cur)
+  {
+#ifdef CHECK_INVARIANTS
+    const uint64_t needed = sizeof(uint32_t) + LZ4_compressBound(sz);
+    INVARIANT(g_buffer_size - cur->curoff_ >= needed);
+#endif
+
+    const int ret = LZ4_compress_heap(
+        lz4ctx,
+        (const char *) p,
+        (char *) cur->pointer() + sizeof(uint32_t),
+        sz);
+
+    INVARIANT(ret >= 0);
+    serializer<uint32_t, false> s_uint32_t;
+    s_uint32_t.write(cur->pointer(), ret);
+    cur->curoff_ += sizeof(uint32_t) + ret;
+    cur->header()->nentries_ += nentries;
+    cur->header()->last_tid_ = lasttid;
+
+    return ret;
+  }
+
+protected:
+  void
+  worker(unsigned id) OVERRIDE
+  {
+    const bool compress = do_compression();
+    uint8_t horizon[g_horizon_size]; // LZ4 looks at 65kb windows
+
+    // where are we in the window, how many elems in this window?
+    size_t horizon_p = 0, horizon_nentries = 0;
+    uint64_t horizon_last_tid = 0; // last committed TID in the horizon
+
+    double cratios = 0.0;
+    unsigned long ncompressions = 0;
+
+    void *lz4ctx = nullptr; // holds a heap-allocated LZ4 hash table
+    if (compress)
+      lz4ctx = LZ4_create();
+
+    mt19937 prng(id);
+
+    // read/write sets are uniform for now
+    uniform_int_distribution<unsigned> dist(0, g_nrecords - 1);
+
+    vector<uint64_t> readset(g_readset);
+    vector<pair<string, string>> writeset(g_writeset);
+    for (auto &pr : writeset) {
+      pr.first.reserve(g_keysize);
+      pr.second.reserve(g_valuesize);
+    }
+
+    struct pbuffer *curbuf = nullptr;
+    uint64_t lasttid = 0,
+             ncommits_currentepoch = 0,
+             ncommits_synced = 0;
+    vector<pair<uint64_t, uint64_t>> outstanding_commits;
+    for (size_t i = 0; i < g_ntxns_worker; i++) {
+
+      // update epoch info
+      const uint64_t lastepoch = per_thread_epochs_[id]->load(memory_order_acquire);
+      const uint64_t curepoch = epoch_number_->load(memory_order_acquire);
+
+      if (lastepoch != curepoch) {
+        // try to sync outstanding commits
+        INVARIANT(curepoch == (lastepoch + 1));
+        const size_t cursyncepoch = system_sync_epoch_->load(memory_order_acquire);
+        ncommits_synced +=
+          inplace_update_persistent_info(outstanding_commits, cursyncepoch);
+
+        // add information about the last epoch
+        outstanding_commits.emplace_back(lastepoch, ncommits_currentepoch);
+        ncommits_currentepoch = 0;
+
+        per_thread_epochs_[id]->store(curepoch, memory_order_release);
+      }
+
+      for (size_t j = 0; j < g_readset; j++)
+        readset[j] = g_database[dist(prng)];
+
+      const uint64_t idmax = tidhelpers::vecidmax(lasttid, readset);
+      // XXX: ignore future epochs for now
+      const uint64_t tidcommit = tidhelpers::MakeTid(id, idmax + 1, curepoch);
+      lasttid = tidcommit;
+
+      for (size_t j = 0; j < g_writeset; j++) {
+        auto idx = dist(prng);
+        g_database[idx] = lasttid;
+        fillkey(writeset[j].first, idx, g_keysize, prng);
+        fillvalue(writeset[j].second, idx, g_valuesize, prng);
+      }
+
+      const uint64_t space_needed = compute_log_record_space();
+      if (compress) {
+        if (horizon_p + space_needed > g_horizon_size) {
+          // need to compress and write horizon
+          curbuf = ensure_buffer_with_space(id, curbuf,
+            sizeof(uint32_t) + LZ4_compressBound(horizon_p));
+
+          const uint64_t compsz =
+            write_horizon(lz4ctx, &horizon[0], horizon_p,
+                          horizon_nentries, horizon_last_tid,
+                          curbuf);
+
+          const double cratio = double(horizon_p) / double(compsz);
+          cratios += cratio;
+          ncompressions++;
+
+          // can reset horizon
+          horizon_p = horizon_nentries = horizon_last_tid = 0;
+        }
+
+        write_log_record(&horizon[0] + horizon_p, tidcommit, readset, writeset);
+        horizon_p += space_needed;
+        horizon_nentries++;
+        horizon_last_tid = tidcommit;
+        ncommits_currentepoch++;
+      } else {
+        curbuf = ensure_buffer_with_space(id, curbuf, space_needed);
+        uint8_t *p = curbuf->pointer();
+        write_log_record(p, tidcommit, readset, writeset);
+        //cerr << "write tidcommit=" << tidhelpers::Str(tidcommit) << endl;
+        curbuf->curoff_ += space_needed;
+        curbuf->header()->nentries_++;
+        curbuf->header()->last_tid_ = tidcommit;
+        ncommits_currentepoch++;
+      }
+    }
+
+    if (compress) {
+      if (horizon_nentries) {
+        curbuf = ensure_buffer_with_space(id, curbuf,
+            sizeof(uint32_t) + LZ4_compressBound(horizon_p));
+
+        const uint64_t compsz =
+          write_horizon(lz4ctx, &horizon[0], horizon_p,
+                        horizon_nentries, horizon_last_tid,
+                        curbuf);
+
+        const double cratio = double(horizon_p) / double(compsz);
+        cratios += cratio;
+        ncompressions++;
+
+        horizon_p = horizon_nentries = horizon_last_tid = 0;
+      }
+      LZ4_free(lz4ctx);
+    }
+
+    if (curbuf) {
+      // XXX: hacky - an agreed upon future epoch for all threads to converge
+      // on upon finishing
+      const uint64_t FutureEpoch = 100000;
+      const uint64_t waitfor = tidhelpers::EpochId(
+          curbuf->header()->last_tid_);
+      INVARIANT(per_thread_epochs_[id]->load(memory_order_acquire) == waitfor);
+      ALWAYS_ASSERT(waitfor < FutureEpoch);
+      curbuf->header()->last_tid_ =
+        tidhelpers::MakeTid(id, 0, FutureEpoch);
+      g_persist_buffers[id].enq(curbuf);
+      outstanding_commits.emplace_back(waitfor, ncommits_currentepoch);
+      //cerr << "worker " << id << " waitfor epoch " << waitfor << endl;
+      // get these commits persisted
+      while (system_sync_epoch_->load(memory_order_acquire) < waitfor)
+        nop_pause();
+      ncommits_synced +=
+        inplace_update_persistent_info(outstanding_commits, waitfor);
+      ALWAYS_ASSERT(outstanding_commits.empty());
+    }
+
+    if (g_verbose && compress)
+      cerr << "Average compression ratio: " << cratios / double(ncompressions) << endl;
+
+    g_ntxns_committed.fetch_add(ncommits_synced, memory_order_release);
+  }
+
+private:
+  void
+  fsyncer(unsigned id, int fd, one_way_post<int> &channel)
+  {
+    for (;;) {
+      int ret;
+      channel.peek(ret);
+      if (ret == -1)
+        return;
+      ret = fdatasync(fd);
+      if (ret == -1) {
+        perror("fdatasync");
+        exit(1);
+      }
+      channel.consume(ret);
+    }
+  }
+
+  void
+  writer(unsigned id, int fd, const vector<unsigned> &assignment)
+  {
+    vector<iovec> iovs(g_nworkers * g_perthread_buffers);
+    vector<pbuffer *> pxs;
+    struct timespec last_io_completed;
+    one_way_post<int> *channel =
+      g_fsync_background ? new one_way_post<int> : nullptr;
+    uint64_t total_nbytes_written = 0,
+             total_txns_written = 0;
+
+    bool sense = false; // cur is at sense, prev is at !sense
+    uint64_t nbytes_written[2], txns_written[2], epoch_prefixes[2][g_nworkers];
+    memset(&nbytes_written[0], 0, sizeof(nbytes_written));
+    memset(&txns_written[0], 0, sizeof(txns_written));
+    memset(&epoch_prefixes[0], 0, sizeof(epoch_prefixes[0]));
+    memset(&epoch_prefixes[1], 0, sizeof(epoch_prefixes[1]));
+
+    clock_gettime(CLOCK_MONOTONIC, &last_io_completed);
+    thread fsync_thread;
+    if (g_fsync_background) {
+      fsync_thread = move(thread(
+            &onecopy_logbased_simulation::fsyncer, this, id, fd, ref(*channel)));
+      fsync_thread.detach();
+    }
+
+    while (keep_going_->load(memory_order_acquire)) {
+
+      // don't allow this loop to proceed less than an epoch's worth of time,
+      // so we can batch IO
+      struct timespec now, diff;
+      clock_gettime(CLOCK_MONOTONIC, &now);
+      timespec_utils::subtract(&now, &last_io_completed, &diff);
+      if (diff.tv_sec == 0 && diff.tv_nsec < long(g_epoch_time_ns)) {
+        // need to sleep it out
+        struct timespec ts;
+        ts.tv_sec = 0;
+        ts.tv_nsec = g_epoch_time_ns - diff.tv_nsec;
+        nanosleep(&ts, nullptr);
+      }
+      clock_gettime(CLOCK_MONOTONIC, &last_io_completed);
+
+      size_t nwritten = 0;
+      nbytes_written[sense] = txns_written[sense] = 0;
+      for (auto idx : assignment) {
+        INVARIANT(idx >= 0 && idx < g_nworkers);
+        g_persist_buffers[idx].peekall(pxs);
+        for (auto px : pxs) {
+          INVARIANT(px);
+          INVARIANT(!px->io_scheduled_);
+          iovs[nwritten].iov_base = (void *) px->buf_.data();
+          iovs[nwritten].iov_len = px->curoff_;
+          nbytes_written[sense] += px->curoff_;
+          px->io_scheduled_ = true;
+          px->curoff_ = sizeof(logbuf_header);
+          px->remaining_ = px->header()->nentries_;
+          txns_written[sense] += px->header()->nentries_;
+          nwritten++;
+          INVARIANT(tidhelpers::CoreId(px->header()->last_tid_) == idx);
+          INVARIANT(epoch_prefixes[sense][idx] <=
+                    tidhelpers::EpochId(px->header()->last_tid_));
+          INVARIANT(tidhelpers::EpochId(px->header()->last_tid_) > 0);
+          epoch_prefixes[sense][idx] =
+            tidhelpers::EpochId(px->header()->last_tid_) - 1;
+        }
+      }
+
+      if (!nwritten) {
+        // XXX: should probably sleep here
+        nop_pause();
+        if (!g_fsync_background || !channel->can_post()) {
+          //cerr << "writer skipping because no work to do" << endl;
+          continue;
+        }
+      }
+
+      //cerr << "writer " << id << " nwritten " << nwritten << endl;
+
+      const ssize_t ret =
+        nwritten ? writev(fd, &iovs[0], nwritten) : 0;
+      if (ret == -1) {
+        perror("writev");
+        exit(1);
+      }
+
+      bool dosense;
+      if (g_fsync_background) {
+        // wait for fsync from the previous write
+        if (nwritten)
+          channel->post(0, true);
+        else
+          INVARIANT(channel->can_post());
+        dosense = !sense;
+      } else {
+        int ret = fdatasync(fd);
+        if (ret == -1) {
+          perror("fdatasync");
+          exit(1);
+        }
+        dosense = sense;
+      }
+
+      // update metadata from previous write
+      for (size_t i = 0; i < g_nworkers; i++) {
+        const uint64_t x0 =
+          per_thread_sync_epochs_[id].epochs_[i].load(memory_order_acquire);
+        const uint64_t x1 = epoch_prefixes[dosense][i];
+        if (x1 > x0)
+          per_thread_sync_epochs_[id].epochs_[i].store(
+              x1, memory_order_release);
+      }
+      total_nbytes_written += nbytes_written[dosense];
+      total_txns_written += txns_written[dosense];
+
+      // bump the sense
+      sense = !sense;
+
+      // return all buffers that have been io_scheduled_ - we can do this as
+      // soon as write returns
+      for (auto idx : assignment) {
+        pbuffer *px;
+        while ((px = g_persist_buffers[idx].peek()) &&
+               px->io_scheduled_) {
+          g_persist_buffers[idx].deq();
+          g_all_buffers[idx].enq(px);
+        }
+      }
+    }
+
+    g_bytes_written[id].store(total_nbytes_written, memory_order_release);
+    g_ntxns_written.fetch_add(total_txns_written, memory_order_release);
+  }
+
+  inline void
+  advance_system_sync_epoch(const vector<vector<unsigned>> &assignments)
+  {
+    uint64_t min_so_far = numeric_limits<uint64_t>::max();
+    for (size_t i = 0; i < assignments.size(); i++)
+      for (auto j : assignments[i])
+        min_so_far =
+          min(per_thread_sync_epochs_[i].epochs_[j].load(memory_order_acquire), min_so_far);
+
+#ifdef CHECK_INVARIANTS
+    const uint64_t syssync = system_sync_epoch_->load(memory_order_acquire);
+    INVARIANT(syssync <= min_so_far);
+#endif
+    system_sync_epoch_->store(min_so_far, memory_order_release);
+  }
+
+public:
+  void
+  logger(const vector<int> &fds,
+         const vector<vector<unsigned>> &assignments_given) OVERRIDE
+  {
+    // compute thread => logger assignment
+    vector<thread> writers;
+    vector<vector<unsigned>> assignments(assignments_given);
+
+    if (assignments.empty()) {
+      // compute assuming homogenous disks
+      if (g_nworkers <= fds.size()) {
+        // each thread gets its own logging worker
+        for (size_t i = 0; i < g_nworkers; i++)
+          assignments.push_back({(unsigned) i});
+      } else {
+        // XXX: currently we assume each logger is equally as fast- we should
+        // adjust ratios accordingly for non-homogenous loggers
+        const size_t threads_per_logger = g_nworkers / fds.size();
+        for (size_t i = 0; i < fds.size(); i++) {
+          assignments.emplace_back(
+            MakeRange<unsigned>(
+                i * threads_per_logger,
+                ((i + 1) == fds.size()) ?
+                  g_nworkers :
+                  (i + 1) * threads_per_logger));
+        }
+      }
+    }
+
+    INVARIANT(AssignmentsValid(assignments, fds.size(), g_nworkers));
+
+    timer tt;
+    for (size_t i = 0; i < assignments.size(); i++)
+      writers.emplace_back(
+        &onecopy_logbased_simulation::writer,
+        this, i, fds[i], ref(assignments[i]));
+    if (g_verbose)
+      cerr << "assignments: " << assignments << endl;
+    while (keep_going_->load(memory_order_acquire)) {
+      // periodically compute which epoch is the persistence epoch,
+      // and update system_sync_epoch_
+
+      struct timespec t;
+      t.tv_sec  = g_epoch_time_ns / ONE_SECOND_NS;
+      t.tv_nsec = g_epoch_time_ns % ONE_SECOND_NS;
+      nanosleep(&t, nullptr);
+
+      advance_system_sync_epoch(assignments);
+    }
+
+    for (auto &t : writers)
+      t.join();
+
+    if (g_verbose) {
+      cerr << "current epoch: " << epoch_number_->load(memory_order_acquire) << endl;
+      cerr << "sync epoch   : " << system_sync_epoch_->load(memory_order_acquire) << endl;
+      const double xsec = tt.lap_ms() / 1000.0;
+      for (size_t i = 0; i < writers.size(); i++)
+        cerr << "writer " << i << " " <<
+          (double(g_bytes_written[i].load(memory_order_acquire)) /
+           double(1UL << 20) /
+           xsec) << " MB/sec" << endl;
+    }
+  }
+
+protected:
+  vector<pbuffer *> pxs_; // just some scratch space
+};
+
+circbuf<pbuffer, onecopy_logbased_simulation::g_perthread_buffers>
+  onecopy_logbased_simulation::g_all_buffers[NMAXCORES];
+circbuf<pbuffer, onecopy_logbased_simulation::g_perthread_buffers>
+  onecopy_logbased_simulation::g_persist_buffers[NMAXCORES];
+
+class explicit_deptracking_simulation : public onecopy_logbased_simulation {
+public:
+
+  /** global state about our persistence calculations */
+
+  // contains the latest TID inclusive, per core, which is (transitively)
+  // persistent. note that the prefix of the DB which is totally persistent is
+  // simply the max of this table.
+  static uint64_t g_persistence_vc[NMAXCORES];
+
+protected:
+
+  bool do_compression() const OVERRIDE { return false; }
+
+  const uint8_t *
+  read_log_entry(const uint8_t *p, uint64_t &tid,
+                 std::function<void(uint64_t)> readfunctor) OVERRIDE
+  {
+    serializer<uint8_t, false> s_uint8_t;
+    serializer<uint64_t, false> s_uint64_t;
+
+    uint8_t readset_sz, writeset_sz, key_sz, value_sz;
+    uint64_t v;
+
+    p = s_uint64_t.read(p, &tid);
+    p = s_uint8_t.read(p, &readset_sz);
+    INVARIANT(size_t(readset_sz) == g_readset);
+    for (size_t i = 0; i < size_t(readset_sz); i++) {
+      p = s_uint64_t.read(p, &v);
+      readfunctor(v);
+    }
+
+    p = s_uint8_t.read(p, &writeset_sz);
+    INVARIANT(size_t(writeset_sz) == g_writeset);
+    for (size_t i = 0; i < size_t(writeset_sz); i++) {
+      p = s_uint8_t.read(p, &key_sz);
+      INVARIANT(size_t(key_sz) == g_keysize);
+      p += size_t(key_sz);
+      p = s_uint8_t.read(p, &value_sz);
+      INVARIANT(size_t(value_sz) == g_valuesize);
+      p += size_t(value_sz);
+    }
+
+    return p;
+  }
+
+  uint64_t
+  compute_log_record_space() const OVERRIDE
+  {
+    // compute how much space we need for this entry
+    uint64_t space_needed = 0;
+
+    // 8 bytes to indicate TID
+    space_needed += sizeof(uint64_t);
+
+    // one byte to indicate # of read deps
+    space_needed += 1;
+
+    // each dep occupies 8 bytes
+    space_needed += g_readset * sizeof(uint64_t);
+
+    // one byte to indicate # of records written
+    space_needed += 1;
+
+    // each record occupies (1 + key_length + 1 + value_length) bytes
+    space_needed += g_writeset * (1 + g_keysize + 1 + g_valuesize);
+
+    return space_needed;
+  }
+
+  void
+  write_log_record(uint8_t *p,
+                   uint64_t tidcommit,
+                   const vector<uint64_t> &readset,
+                   const vector<pair<string, string>> &writeset) OVERRIDE
+  {
+    serializer<uint8_t, false> s_uint8_t;
+    serializer<uint64_t, false> s_uint64_t;
+
+    p = s_uint64_t.write(p, tidcommit);
+    p = s_uint8_t.write(p, readset.size());
+    for (auto t : readset)
+      p = s_uint64_t.write(p, t);
+    p = s_uint8_t.write(p, writeset.size());
+    for (auto &pr : writeset) {
+      p = s_uint8_t.write(p, pr.first.size());
+      memcpy(p, pr.first.data(), pr.first.size()); p += pr.first.size();
+      p = s_uint8_t.write(p, pr.second.size());
+      memcpy(p, pr.second.data(), pr.second.size()); p += pr.second.size();
+    }
+  }
+
+  void
+  logger_on_io_completion() OVERRIDE
+  {
+    ALWAYS_ASSERT(false); // currently broken
+    bool changed = true;
+    while (changed) {
+      changed = false;
+      for (size_t i = 0; i < NMAXCORES; i++) {
+        g_persist_buffers[i].peekall(pxs_);
+        for (auto px : pxs_) {
+          INVARIANT(px);
+          if (!px->io_scheduled_)
+            break;
+
+          INVARIANT(px->remaining_ > 0);
+          INVARIANT(px->curoff_ < g_buffer_size);
+
+          const uint8_t *p = px->pointer();
+          uint64_t committid;
+          bool allsat = true;
+
+          //cerr << "processing buffer " << px << " with curoff_=" << px->curoff_ << endl
+          //     << "  p=" << intptr_t(p) << endl;
+
+          while (px->remaining_ && allsat) {
+            allsat = true;
+            const uint8_t *nextp =
+              read_log_entry(p, committid, [&allsat](uint64_t readdep) {
+                if (!allsat)
+                  return;
+                const uint64_t cid = tidhelpers::CoreId(readdep);
+                if (readdep > g_persistence_vc[cid])
+                  allsat = false;
+              });
+            if (allsat) {
+              //cerr << "committid=" << tidhelpers::Str(committid)
+              //     << ", g_persistence_vc=" << tidhelpers::Str(g_persistence_vc[i])
+              //     << endl;
+              INVARIANT(tidhelpers::CoreId(committid) == i);
+              INVARIANT(g_persistence_vc[i] < committid);
+              g_persistence_vc[i] = committid;
+              changed = true;
+              p = nextp;
+              px->remaining_--;
+              px->curoff_ = intptr_t(p) - intptr_t(px->buf_.data());
+              g_ntxns_committed++;
+            } else {
+              // done, no further entries will be satisfied
+            }
+          }
+
+          if (allsat) {
+            INVARIANT(px->remaining_ == 0);
+            // finished entire buffer
+            struct pbuffer *pxcheck = g_persist_buffers[i].deq();
+            if (pxcheck != px)
+              INVARIANT(false);
+            g_all_buffers[i].enq(px);
+            //cerr << "buffer flused at g_persistence_vc=" << tidhelpers::Str(g_persistence_vc[i]) << endl;
+          } else {
+            INVARIANT(px->remaining_ > 0);
+            break; // cannot process core's list any further
+          }
+        }
+      }
+    }
+  }
+
+};
+
+uint64_t explicit_deptracking_simulation::g_persistence_vc[NMAXCORES] = {0};
+
+class epochbased_simulation : public onecopy_logbased_simulation {
+public:
+  epochbased_simulation(bool compress)
+    : compress_(compress)
+  {
+  }
+
+protected:
+  bool do_compression() const OVERRIDE { return compress_; }
+
+protected:
+  const uint8_t *
+  read_log_entry(const uint8_t *p, uint64_t &tid,
+                 std::function<void(uint64_t)> readfunctor) OVERRIDE
+  {
+    serializer<uint8_t, false> s_uint8_t;
+    serializer<uint64_t, false> s_uint64_t;
+
+    uint8_t writeset_sz, key_sz, value_sz;
+
+    p = s_uint64_t.read(p, &tid);
+    p = s_uint8_t.read(p, &writeset_sz);
+    INVARIANT(size_t(writeset_sz) == g_writeset);
+    for (size_t i = 0; i < size_t(writeset_sz); i++) {
+      p = s_uint8_t.read(p, &key_sz);
+      INVARIANT(size_t(key_sz) == g_keysize);
+      p += size_t(key_sz);
+      p = s_uint8_t.read(p, &value_sz);
+      INVARIANT(size_t(value_sz) == g_valuesize);
+      p += size_t(value_sz);
+    }
+
+    return p;
+  }
+
+  uint64_t
+  compute_log_record_space() const OVERRIDE
+  {
+    // compute how much space we need for this entry
+    uint64_t space_needed = 0;
+
+    // 8 bytes to indicate TID
+    space_needed += sizeof(uint64_t);
+
+    // one byte to indicate # of records written
+    space_needed += 1;
+
+    // each record occupies (1 + key_length + 1 + value_length) bytes
+    space_needed += g_writeset * (1 + g_keysize + 1 + g_valuesize);
+
+    return space_needed;
+  }
+
+  void
+  write_log_record(uint8_t *p,
+                   uint64_t tidcommit,
+                   const vector<uint64_t> &readset,
+                   const vector<pair<string, string>> &writeset) OVERRIDE
+  {
+    serializer<uint8_t, false> s_uint8_t;
+    serializer<uint64_t, false> s_uint64_t;
+
+    p = s_uint64_t.write(p, tidcommit);
+    p = s_uint8_t.write(p, writeset.size());
+    for (auto &pr : writeset) {
+      p = s_uint8_t.write(p, pr.first.size());
+      memcpy(p, pr.first.data(), pr.first.size()); p += pr.first.size();
+      p = s_uint8_t.write(p, pr.second.size());
+      memcpy(p, pr.second.data(), pr.second.size()); p += pr.second.size();
+    }
+  }
+
+private:
+  bool compress_;
+};
+
+int
+main(int argc, char **argv)
+{
+  string strategy = "epoch";
+  vector<string> logfiles;
+  vector<vector<unsigned>> assignments;
+
+  while (1) {
+    static struct option long_options[] =
+    {
+      {"verbose"     , no_argument       , &g_verbose , 1}   ,
+      {"fsync-back"  , no_argument       , &g_fsync_background, 1},
+      {"num-threads" , required_argument , 0          , 't'} ,
+      {"strategy"    , required_argument , 0          , 's'} ,
+      {"readset"     , required_argument , 0          , 'r'} ,
+      {"writeset"    , required_argument , 0          , 'w'} ,
+      {"keysize"     , required_argument , 0          , 'k'} ,
+      {"valuesize"   , required_argument , 0          , 'v'} ,
+      {"logfile"     , required_argument , 0          , 'l'} ,
+      {"assignment"  , required_argument , 0          , 'a'} ,
+      {0, 0, 0, 0}
+    };
+    int option_index = 0;
+    int c = getopt_long(argc, argv, "t:s:r:w:k:v:l:a:", long_options, &option_index);
+    if (c == -1)
+      break;
+
+    switch (c) {
+    case 0:
+      if (long_options[option_index].flag != 0)
+        break;
+      abort();
+      break;
+
+    case 't':
+      g_nworkers = strtoul(optarg, nullptr, 10);
+      break;
+
+    case 's':
+      strategy = optarg;
+      break;
+
+    case 'r':
+      g_readset = strtoul(optarg, nullptr, 10);
+      break;
+
+    case 'w':
+      g_writeset = strtoul(optarg, nullptr, 10);
+      break;
+
+    case 'k':
+      g_keysize = strtoul(optarg, nullptr, 10);
+      break;
+
+    case 'v':
+      g_valuesize = strtoul(optarg, nullptr, 10);
+      break;
+
+    case 'l':
+      logfiles.emplace_back(optarg);
+      break;
+
+    case 'a':
+      assignments.emplace_back(
+          ParseCSVString<unsigned, RangeAwareParser<unsigned>>(optarg));
+      break;
+
+    case '?':
+      /* getopt_long already printed an error message. */
+      exit(1);
+
+    default:
+      abort();
+    }
+  }
+  ALWAYS_ASSERT(g_nworkers >= 1);
+  ALWAYS_ASSERT(g_readset >= 0);
+  ALWAYS_ASSERT(g_writeset > 0);
+  ALWAYS_ASSERT(g_keysize > 0);
+  ALWAYS_ASSERT(g_valuesize >= 0);
+  ALWAYS_ASSERT(!logfiles.empty());
+  ALWAYS_ASSERT(logfiles.size() <= g_nmax_loggers);
+  ALWAYS_ASSERT(
+      assignments.empty() ||
+      database_simulation::AssignmentsValid(
+        assignments, logfiles.size(), g_nworkers));
+
+  if (g_verbose)
+    cerr << "{nworkers=" << g_nworkers
+         << ", readset=" << g_readset
+         << ", writeset=" << g_writeset
+         << ", keysize=" << g_keysize
+         << ", valuesize=" << g_valuesize
+         << ", logfiles=" << logfiles
+         << ", strategy=" << strategy
+         << ", fsync_background=" << g_fsync_background
+         << ", assignments=" << assignments
+         << "}" << endl;
+
+  if (strategy != "deptracking" &&
+      strategy != "epoch" &&
+      strategy != "epoch-compress")
+    ALWAYS_ASSERT(false);
+
+  g_database.resize(g_nrecords); // all start at TID=0
+
+  vector<int> fds;
+  for (auto &fname : logfiles) {
+    int fd = open(fname.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0664);
+    if (fd == -1) {
+      perror("open");
+      return 1;
+    }
+    fds.push_back(fd);
+  }
+
+  unique_ptr<database_simulation> sim;
+  if (strategy == "deptracking")
+    sim.reset(new explicit_deptracking_simulation);
+  else if (strategy == "epoch")
+    sim.reset(new epochbased_simulation(false));
+  else if (strategy == "epoch-compress")
+    sim.reset(new epochbased_simulation(true));
+  else
+    ALWAYS_ASSERT(false);
+  sim->init();
+
+  thread logger_thread(
+      &database_simulation::logger, sim.get(), fds, ref(assignments));
+
+  vector<thread> workers;
+  util::timer tt, tt1;
+  for (size_t i = 0; i < g_nworkers; i++)
+    workers.emplace_back(&database_simulation::worker, sim.get(), i);
+  for (auto &p: workers)
+    p.join();
+  sim->terminate();
+  logger_thread.join();
+
+  const double ntxns_committed = g_ntxns_committed.load();
+  const double xsec = tt.lap_ms() / 1000.0;
+  const double rate = double(ntxns_committed) / xsec;
+  if (g_verbose) {
+    cerr << "txns commited rate: " << rate << " txns/sec" << endl;
+    cerr << "  (" << size_t(ntxns_committed) << " in " << xsec << " sec)" << endl;
+
+    const double ntxns_written = g_ntxns_written.load();
+    const double rate1 = double(ntxns_written) / xsec;
+    cerr << "txns written rate: " << rate1 << " txns/sec" << endl;
+    cerr << "  (" << size_t(ntxns_written) << " in " << xsec << " sec)" << endl;
+  } else {
+    cout << rate << endl;
+  }
+
+  return 0;
+}
diff --git a/silo/prefetch.h b/silo/prefetch.h
new file mode 100644 (file)
index 0000000..5e6c1a3
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef _PREFETCH_H_
+#define _PREFETCH_H_
+
+#include <algorithm>
+
+#include "util.h"
+#include "macros.h"
+
+#if !MASSTREE_COMPILER_HH
+// assumes cache-aligned
+static inline ALWAYS_INLINE void
+prefetch(const void *ptr)
+{
+  typedef struct { char x[CACHELINE_SIZE]; } cacheline_t;
+  asm volatile("prefetcht0 %0" : : "m" (*(const cacheline_t *) ptr));
+}
+#define PREFETCH_DEFINED 1
+#endif
+
+// assumes cache-aligned
+template <typename T>
+static inline ALWAYS_INLINE void
+prefetch_object(const T *ptr)
+{
+  for (unsigned i = CACHELINE_SIZE;
+       i < std::min(static_cast<unsigned>(sizeof(*ptr)),
+                    static_cast<unsigned>(4 * CACHELINE_SIZE));
+       i += CACHELINE_SIZE)
+    prefetch((const char *) ptr + i);
+}
+
+// prefetch an object resident in [ptr, ptr + n). doesn't assume cache aligned
+static inline ALWAYS_INLINE void
+prefetch_bytes(const void *p, size_t n)
+{
+  const char *ptr = (const char *) p;
+  // round down to nearest cacheline, then prefetch
+  const void * const pend =
+    std::min(ptr + n,  ptr + 4 * CACHELINE_SIZE);
+  ptr = (const char *) util::round_down<uintptr_t, LG_CACHELINE_SIZE>((uintptr_t) ptr);
+
+  // manually unroll loop 3 times
+  ptr += CACHELINE_SIZE;
+  if (ptr < pend)
+    prefetch(ptr);
+  ptr += CACHELINE_SIZE;
+  if (ptr < pend)
+    prefetch(ptr);
+  ptr += CACHELINE_SIZE;
+  if (ptr < pend)
+    prefetch(ptr);
+}
+
+#endif /* _PREFETCH_H_ */
diff --git a/silo/pxqueue.h b/silo/pxqueue.h
new file mode 100644 (file)
index 0000000..8c68739
--- /dev/null
@@ -0,0 +1,354 @@
+#pragma once
+
+#include <iterator>
+#include <string>
+
+#include "counter.h"
+#include "macros.h"
+#include "util.h"
+
+// abstract queue for RCU-like queues
+
+// forward decl
+template <typename T, size_t N> struct basic_px_queue;
+
+template <typename T, size_t N>
+struct basic_px_group {
+  basic_px_group()
+    : next_(nullptr), rcu_tick_(0)
+  {
+    static event_counter evt_px_group_creates(
+        util::cxx_typename<T>::value() + std::string("_px_group_creates"));
+    ++evt_px_group_creates;
+  }
+  ~basic_px_group()
+  {
+    static event_counter evt_px_group_deletes(
+        util::cxx_typename<T>::value() + std::string("_px_group_deletes"));
+    ++evt_px_group_deletes;
+  }
+
+  // no copying/moving
+  basic_px_group(const basic_px_group &) = delete;
+  basic_px_group &operator=(const basic_px_group &) = delete;
+  basic_px_group(basic_px_group &&) = delete;
+
+  static const size_t GroupSize = N;
+  friend class basic_px_queue<T, N>;
+
+private:
+  basic_px_group *next_;
+  typename util::vec<T, GroupSize>::type pxs_;
+  uint64_t rcu_tick_; // all elements in pxs_ are from this tick,
+                      // this number is only meaningful if pxs_ is
+                      // not empty
+};
+
+// not thread safe- should guard with lock for concurrent manipulation
+template <typename T, size_t N>
+struct basic_px_queue {
+  basic_px_queue()
+    : head_(nullptr), tail_(nullptr),
+      freelist_head_(nullptr), freelist_tail_(nullptr),
+      ngroups_(0) {}
+
+  typedef basic_px_group<T, N> px_group;
+
+  basic_px_queue(basic_px_queue &&) = default;
+  basic_px_queue(const basic_px_queue &) = delete;
+  basic_px_queue &operator=(const basic_px_queue &) = delete;
+
+  ~basic_px_queue()
+  {
+    reap_chain(head_);
+    reap_chain(freelist_head_);
+  }
+
+  void
+  swap(basic_px_queue &other)
+  {
+    std::swap(head_, other.head_);
+    std::swap(tail_, other.tail_);
+    std::swap(freelist_head_, other.freelist_head_);
+    std::swap(freelist_tail_, other.freelist_tail_);
+    std::swap(ngroups_, other.ngroups_);
+  }
+
+  template <typename PtrType, typename ObjType>
+  class iterator_ : public std::iterator<std::forward_iterator_tag, ObjType> {
+  public:
+    inline iterator_() : px_(nullptr), i_() {}
+    inline iterator_(PtrType *px) : px_(px), i_() {}
+
+    // allow iterator to assign to const_iterator
+    template <typename P, typename O>
+    inline iterator_(const iterator_<P, O> &o) : px_(o.px_), i_(o.i_) {}
+
+    inline ObjType &
+    operator*() const
+    {
+      return px_->pxs_[i_];
+    }
+
+    inline ObjType *
+    operator->() const
+    {
+      return &px_->pxs_[i_];
+    }
+
+    inline uint64_t
+    tick() const
+    {
+      return px_->rcu_tick_;
+    }
+
+    inline bool
+    operator==(const iterator_ &o) const
+    {
+      return px_ == o.px_ && i_ == o.i_;
+    }
+
+    inline bool
+    operator!=(const iterator_ &o) const
+    {
+      return !operator==(o);
+    }
+
+    inline iterator_ &
+    operator++()
+    {
+      ++i_;
+      if (i_ == px_->pxs_.size()) {
+        px_ = px_->next_;
+        i_ = 0;
+      }
+      return *this;
+    }
+
+    inline iterator_
+    operator++(int)
+    {
+      iterator_ cur = *this;
+      ++(*this);
+      return cur;
+    }
+
+  private:
+    PtrType *px_;
+    size_t i_;
+  };
+
+  typedef iterator_<px_group, T> iterator;
+  typedef iterator_<const px_group, const T> const_iterator;
+
+  inline iterator begin() { return iterator(head_); }
+  inline const_iterator begin() const { return iterator(head_); }
+
+  inline iterator end() { return iterator(nullptr); }
+  inline const_iterator end() const { return iterator(nullptr); }
+
+  // enqueue t in epoch rcu_tick
+  // assumption: rcu_ticks can only go up!
+  void
+  enqueue(const T &t, uint64_t rcu_tick)
+  {
+    INVARIANT(bool(head_) == bool(tail_));
+    INVARIANT(bool(head_) == bool(ngroups_));
+    INVARIANT(!tail_ || tail_->pxs_.size() <= px_group::GroupSize);
+    INVARIANT(!tail_ || tail_->rcu_tick_ <= rcu_tick);
+    px_group *g;
+    if (unlikely(!tail_ ||
+                 tail_->pxs_.size() == px_group::GroupSize ||
+                 tail_->rcu_tick_ != rcu_tick)) {
+      ensure_freelist();
+      // pop off freelist
+      g = freelist_head_;
+      freelist_head_ = g->next_;
+      if (g == freelist_tail_)
+        freelist_tail_ = nullptr;
+      g->next_ = nullptr;
+      g->pxs_.clear();
+      g->rcu_tick_ = rcu_tick;
+      ngroups_++;
+
+      // adjust ptrs
+      if (!head_) {
+        head_ = tail_ = g;
+      } else {
+        tail_->next_ = g;
+        tail_ = g;
+      }
+    } else {
+      g = tail_;
+    }
+    INVARIANT(g->pxs_.size() < px_group::GroupSize);
+    INVARIANT(g->rcu_tick_ == rcu_tick);
+    INVARIANT(!g->next_);
+    INVARIANT(tail_ == g);
+    g->pxs_.emplace_back(t);
+    sanity_check();
+  }
+
+  void
+  ensure_freelist()
+  {
+    INVARIANT(bool(freelist_head_) == bool(freelist_tail_));
+    if (likely(freelist_head_))
+      return;
+    const size_t nalloc = 16;
+    alloc_freelist(nalloc);
+  }
+
+  inline bool
+  empty() const
+  {
+    return !head_;
+  }
+
+#ifdef CHECK_INVARIANTS
+  void
+  sanity_check() const
+  {
+    INVARIANT(bool(head_) == bool(tail_));
+    INVARIANT(!tail_ || ngroups_);
+    INVARIANT(!tail_ || !tail_->next_);
+    INVARIANT(bool(freelist_head_) == bool(freelist_tail_));
+    INVARIANT(!freelist_tail_ || !freelist_tail_->next_);
+    px_group *p = head_, *pprev = nullptr;
+    size_t n = 0;
+    uint64_t prev_tick = 0;
+    while (p) {
+      INVARIANT(p->pxs_.size());
+      INVARIANT(p->pxs_.size() <= px_group::GroupSize);
+      INVARIANT(prev_tick <= p->rcu_tick_);
+      prev_tick = p->rcu_tick_;
+      pprev = p;
+      p = p->next_;
+      n++;
+    }
+    INVARIANT(n == ngroups_);
+    INVARIANT(!pprev || tail_ == pprev);
+    p = freelist_head_;
+    pprev = nullptr;
+    while (p) {
+      pprev = p;
+      p = p->next_;
+    }
+    INVARIANT(!pprev || freelist_tail_ == pprev);
+  }
+#else
+  inline ALWAYS_INLINE void sanity_check() const {}
+#endif
+
+  // assumes this instance is EMPTY, accept from source
+  // all entries <= e
+  inline void
+  empty_accept_from(basic_px_queue &source, uint64_t rcu_tick)
+  {
+    ALWAYS_ASSERT(empty());
+    INVARIANT(this != &source);
+    INVARIANT(!tail_);
+    px_group *p = source.head_, *pnext;
+    while (p && p->rcu_tick_ <= rcu_tick) {
+      pnext = p->next_;
+      p->next_ = nullptr;
+      if (!head_) {
+        head_ = tail_ = p;
+      } else {
+        tail_->next_ = p;
+        tail_ = p;
+      }
+      ngroups_++;
+      source.ngroups_--;
+      source.head_ = p = pnext;
+      if (!source.head_)
+        source.tail_ = nullptr;
+    }
+    sanity_check();
+    source.sanity_check();
+  }
+
+  // transfer *this* elements freelist to dest
+  void
+  transfer_freelist(basic_px_queue &dest, ssize_t n = -1)
+  {
+    if (!freelist_head_)
+      return;
+    if (n < 0) {
+      freelist_tail_->next_ = dest.freelist_head_;
+      if (!dest.freelist_tail_)
+        dest.freelist_tail_ = freelist_tail_;
+      dest.freelist_head_ = freelist_head_;
+      freelist_head_ = freelist_tail_ = nullptr;
+    } else {
+      px_group *p = freelist_head_;
+      size_t c = 0;
+      while (p && c++ < static_cast<size_t>(n)) {
+        px_group *tmp = p->next_;
+        p->next_ = dest.freelist_head_;
+        dest.freelist_head_ = p;
+        if (!dest.freelist_tail_)
+          dest.freelist_tail_ = p;
+        if (p == freelist_tail_)
+          freelist_tail_ = nullptr;
+        p = tmp;
+        freelist_head_ = p;
+      }
+    }
+    sanity_check();
+  }
+
+  void
+  clear()
+  {
+    if (!head_)
+      return;
+    tail_->next_ = freelist_head_;
+    if (!freelist_tail_)
+      freelist_tail_ = tail_;
+    freelist_head_ = head_;
+    head_ = tail_ = nullptr;
+    ngroups_ = 0;
+  }
+
+  // adds n new groups to the freelist
+  void
+  alloc_freelist(size_t n)
+  {
+    for (size_t i = 0; i < n; i++) {
+      px_group *p = new px_group;
+      if (!freelist_tail_)
+        freelist_tail_ = p;
+      p->next_ = freelist_head_;
+      freelist_head_ = p;
+    }
+  }
+
+  inline size_t get_ngroups() const { return ngroups_; }
+
+  inline bool
+  get_latest_epoch(uint64_t &e) const
+  {
+    if (!tail_)
+      return false;
+    e = tail_->rcu_tick_;
+    return true;
+  }
+
+private:
+  void
+  reap_chain(px_group *px)
+  {
+    while (px) {
+      px_group *tmp = px->next_;
+      delete px;
+      px = tmp;
+    }
+  }
+
+  px_group *head_;
+  px_group *tail_;
+  px_group *freelist_head_;
+  px_group *freelist_tail_;
+  size_t ngroups_;
+};
diff --git a/silo/rcu.cc b/silo/rcu.cc
new file mode 100644 (file)
index 0000000..946008e
--- /dev/null
@@ -0,0 +1,390 @@
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <numa.h>
+#include <sched.h>
+#include <iostream>
+#include <thread>
+#include <atomic>
+
+#include "rcu.h"
+#include "macros.h"
+#include "util.h"
+#include "thread.h"
+#include "counter.h"
+#include "lockguard.h"
+
+using namespace std;
+using namespace util;
+
+rcu rcu::s_instance;
+
+static event_counter evt_rcu_deletes("rcu_deletes");
+static event_counter evt_rcu_frees("rcu_frees");
+static event_counter evt_rcu_local_reaps("rcu_local_reaps");
+static event_counter evt_rcu_incomplete_local_reaps("rcu_incomplete_local_reaps");
+static event_counter evt_rcu_loop_reaps("rcu_loop_reaps");
+static event_counter *evt_allocator_arena_allocations[::allocator::MAX_ARENAS] = {nullptr};
+static event_counter *evt_allocator_arena_deallocations[::allocator::MAX_ARENAS] = {nullptr};
+static event_counter evt_allocator_large_allocation("allocator_large_allocation");
+
+static event_avg_counter evt_avg_gc_reaper_queue_len("avg_gc_reaper_queue_len");
+static event_avg_counter evt_avg_rcu_delete_queue_len("avg_rcu_delete_queue_len");
+static event_avg_counter evt_avg_rcu_local_delete_queue_len("avg_rcu_local_delete_queue_len");
+static event_avg_counter evt_avg_rcu_sync_try_release("avg_rcu_sync_try_release");
+static event_avg_counter evt_avg_time_inbetween_rcu_epochs_usec(
+    "avg_time_inbetween_rcu_epochs_usec");
+static event_avg_counter evt_avg_time_inbetween_allocator_releases_usec(
+    "avg_time_inbetween_allocator_releases_usec");
+
+#ifdef MEMCHECK_MAGIC
+static void
+report_error_and_die(
+    const void *p, size_t alloc_size, const char *msg,
+    const string &prefix="", unsigned recurse=3, bool first=true)
+{
+  // print the entire allocation block, for debugging reference
+  static_assert(::allocator::AllocAlignment % 8 == 0, "xx");
+  const void *pnext = *((const void **) p);
+  cerr << prefix << "Address " << p << " error found! (next ptr " << pnext << ")" << endl;
+  if (pnext) {
+    const ::allocator::pgmetadata *pmd = ::allocator::PointerToPgMetadata(pnext);
+    if (!pmd) {
+      cerr << prefix << "Error: could not get pgmetadata for next ptr" << endl;
+      cerr << prefix << "Allocator managed next ptr? " << ::allocator::ManagesPointer(pnext) << endl;
+    } else {
+      cerr << prefix << "Next ptr allocation size: " << pmd->unit_ << endl;
+      if (((uintptr_t)pnext % pmd->unit_) == 0) {
+        if (recurse)
+          report_error_and_die(
+              pnext, pmd->unit_, "", prefix + "    ", recurse - 1, false);
+        else
+          cerr << prefix << "recursion stopped" << endl;
+      } else {
+        cerr << prefix << "Next ptr not properly aligned" << endl;
+        if (recurse)
+          report_error_and_die(
+              (const void *) slow_round_down((uintptr_t)pnext, (uintptr_t)pmd->unit_),
+              pmd->unit_, "", prefix + "    ", recurse - 1, false);
+        else
+          cerr << prefix << "recursion stopped" << endl;
+      }
+    }
+  }
+  cerr << prefix << "Msg: " << msg << endl;
+  cerr << prefix << "Allocation size: " << alloc_size << endl;
+  cerr << prefix << "Ptr aligned properly? " << (((uintptr_t)p % alloc_size) == 0) << endl;
+  for (const char *buf = (const char *) p;
+      buf < (const char *) p + alloc_size;
+      buf += 8) {
+    cerr << prefix << hexify_buf(buf, 8) << endl;
+  }
+  if (first)
+    ALWAYS_ASSERT(false);
+}
+
+static void
+check_pointer_or_die(void *p, size_t alloc_size)
+{
+  ALWAYS_ASSERT(p);
+  if (unlikely(((uintptr_t)p % alloc_size) != 0))
+    report_error_and_die(p, alloc_size, "pointer not properly aligned");
+  for (size_t off = sizeof(void **); off < alloc_size; off++)
+    if (unlikely(
+         (unsigned char) *((const char *) p + off) !=
+         (unsigned char) MEMCHECK_MAGIC ) )
+      report_error_and_die(p, alloc_size, "memory magic not found");
+  void *pnext = *((void **) p);
+  if (unlikely(((uintptr_t)pnext % alloc_size) != 0))
+    report_error_and_die(p, alloc_size, "next pointer not properly aligned");
+}
+#endif
+
+void *
+rcu::sync::alloc(size_t sz)
+{
+  if (pin_cpu_ == -1)
+    // fallback to regular allocator
+    return malloc(sz);
+  auto sizes = ::allocator::ArenaSize(sz);
+  auto arena = sizes.second;
+  if (arena >= ::allocator::MAX_ARENAS) {
+    // fallback to regular allocator
+    ++evt_allocator_large_allocation;
+    return malloc(sz);
+  }
+  ensure_arena(arena);
+  void *p = arenas_[arena];
+  INVARIANT(p);
+#ifdef MEMCHECK_MAGIC
+  const size_t alloc_size = (arena + 1) * ::allocator::AllocAlignment;
+  check_pointer_or_die(p, alloc_size);
+#endif
+  arenas_[arena] = *reinterpret_cast<void **>(p);
+  evt_allocator_arena_allocations[arena]->inc();
+  return p;
+}
+
+void *
+rcu::sync::alloc_static(size_t sz)
+{
+  if (pin_cpu_ == -1)
+    return malloc(sz);
+  // round up to hugepagesize
+  static const size_t hugepgsize = ::allocator::GetHugepageSize();
+  sz = slow_round_up(sz, hugepgsize);
+  INVARIANT((sz % hugepgsize) == 0);
+  return ::allocator::AllocateUnmanaged(pin_cpu_, sz / hugepgsize);
+}
+
+void
+rcu::sync::dealloc(void *p, size_t sz)
+{
+  if (!::allocator::ManagesPointer(p)) {
+    ::free(p);
+    return;
+  }
+  auto sizes = ::allocator::ArenaSize(sz);
+  auto arena = sizes.second;
+  ALWAYS_ASSERT(arena < ::allocator::MAX_ARENAS);
+  *reinterpret_cast<void **>(p) = arenas_[arena];
+#ifdef MEMCHECK_MAGIC
+  const size_t alloc_size = (arena + 1) * ::allocator::AllocAlignment;
+  ALWAYS_ASSERT( ((uintptr_t)p % alloc_size) == 0 );
+  NDB_MEMSET(
+      (char *) p + sizeof(void **),
+      MEMCHECK_MAGIC, alloc_size - sizeof(void **));
+  ALWAYS_ASSERT(*((void **) p) == arenas_[arena]);
+  check_pointer_or_die(p, alloc_size);
+#endif
+  arenas_[arena] = p;
+  evt_allocator_arena_deallocations[arena]->inc();
+  deallocs_[arena]++;
+}
+
+bool
+rcu::sync::try_release()
+{
+  // XXX: tune
+  static const size_t threshold = 10000;
+  // only release if there are > threshold segments to release (over all arenas)
+  size_t acc = 0;
+  for (size_t i = 0; i < ::allocator::MAX_ARENAS; i++)
+    acc += deallocs_[i];
+  if (acc > threshold) {
+    do_release();
+    evt_avg_rcu_sync_try_release.offer(acc);
+    return true;
+  }
+  return false;
+}
+
+void
+rcu::sync::do_release()
+{
+#ifdef MEMCHECK_MAGIC
+  for (size_t i = 0; i < ::allocator::MAX_ARENAS; i++) {
+    const size_t alloc_size = (i + 1) * ::allocator::AllocAlignment;
+    void *p = arenas_[i];
+    while (p) {
+      check_pointer_or_die(p, alloc_size);
+      p = *((void **) p);
+    }
+  }
+#endif
+  ::allocator::ReleaseArenas(&arenas_[0]);
+  NDB_MEMSET(&arenas_[0], 0, sizeof(arenas_));
+  NDB_MEMSET(&deallocs_[0], 0, sizeof(deallocs_));
+}
+
+void
+rcu::sync::do_cleanup()
+{
+  // compute cleaner epoch
+  const uint64_t clean_tick_exclusive = impl_->cleaning_rcu_tick_exclusive();
+  if (!clean_tick_exclusive)
+    return;
+  const uint64_t clean_tick = clean_tick_exclusive - 1;
+
+  INVARIANT(last_reaped_epoch_ <= clean_tick);
+  INVARIANT(scratch_.empty());
+  if (last_reaped_epoch_ == clean_tick)
+    return;
+
+#ifdef ENABLE_EVENT_COUNTERS
+  const uint64_t now = timer::cur_usec();
+  if (last_reaped_timestamp_us_ > 0) {
+    const uint64_t diff = now - last_reaped_timestamp_us_;
+    evt_avg_time_inbetween_rcu_epochs_usec.offer(diff);
+  }
+  last_reaped_timestamp_us_ = now;
+#endif
+  last_reaped_epoch_ = clean_tick;
+
+  scratch_.empty_accept_from(queue_, clean_tick);
+  scratch_.transfer_freelist(queue_);
+  rcu::px_queue &q = scratch_;
+  if (q.empty())
+    return;
+  scoped_rcu_region guard;
+  size_t n = 0;
+  for (auto it = q.begin(); it != q.end(); ++it, ++n) {
+    try {
+      it->run(*this);
+    } catch (...) {
+      cerr << "rcu::region_end: uncaught exception in free routine" << endl;
+    }
+  }
+  q.clear();
+  evt_rcu_deletes += n;
+  evt_avg_rcu_local_delete_queue_len.offer(n);
+
+  // try to release memory from allocator slabs back
+  if (try_release()) {
+#ifdef ENABLE_EVENT_COUNTERS
+    const uint64_t now = timer::cur_usec();
+    if (last_release_timestamp_us_ > 0) {
+      const uint64_t diff = now - last_release_timestamp_us_;
+      evt_avg_time_inbetween_allocator_releases_usec.offer(diff);
+    }
+    last_release_timestamp_us_ = now;
+#endif
+  }
+}
+
+void
+rcu::free_with_fn(void *p, deleter_t fn)
+{
+  sync &s = mysync();
+  uint64_t cur_tick = 0; // ticker units
+  const bool is_guarded = ticker::s_instance.is_locally_guarded(cur_tick);
+  if (!is_guarded)
+    INVARIANT(false);
+  INVARIANT(s.depth());
+  // all threads are either at cur_tick or cur_tick + 1, so we must wait for
+  // the system to move beyond cur_tick + 1
+  s.queue_.enqueue(delete_entry(p, fn), to_rcu_ticks(cur_tick + 1));
+  ++evt_rcu_frees;
+}
+
+void
+rcu::dealloc_rcu(void *p, size_t sz)
+{
+  sync &s = mysync();
+  uint64_t cur_tick = 0; // ticker units
+  const bool is_guarded = ticker::s_instance.is_locally_guarded(cur_tick);
+  if (!is_guarded)
+    INVARIANT(false);
+  INVARIANT(s.depth());
+  // all threads are either at cur_tick or cur_tick + 1, so we must wait for
+  // the system to move beyond cur_tick + 1
+  s.queue_.enqueue(delete_entry(p, sz), to_rcu_ticks(cur_tick + 1));
+  ++evt_rcu_frees;
+}
+
+void
+rcu::pin_current_thread(size_t cpu)
+{
+  sync &s = mysync();
+  s.set_pin_cpu(cpu);
+  auto node = numa_node_of_cpu(cpu);
+  // pin to node
+  ALWAYS_ASSERT(!numa_run_on_node(node));
+  // is numa_run_on_node() guaranteed to take effect immediately?
+  ALWAYS_ASSERT(!sched_yield());
+  // release current thread-local cache back to allocator
+  s.do_release();
+}
+
+void
+rcu::fault_region()
+{
+  sync &s = mysync();
+  if (s.get_pin_cpu() == -1)
+    return;
+  ::allocator::FaultRegion(s.get_pin_cpu());
+}
+
+rcu::rcu()
+  : syncs_()
+{
+  // XXX: these should really be instance members of RCU
+  // we are assuming only one rcu object is ever created
+  for (size_t i = 0; i < ::allocator::MAX_ARENAS; i++) {
+    evt_allocator_arena_allocations[i] =
+      new event_counter("allocator_arena" + to_string(i) + "_allocation");
+    evt_allocator_arena_deallocations[i] =
+      new event_counter("allocator_arena" + to_string(i) + "_deallocation");
+  }
+}
+
+struct rcu_stress_test_rec {
+  uint64_t magic_;
+  uint64_t counter_;
+} PACKED;
+
+static const uint64_t rcu_stress_test_magic = 0xABCDDEAD01234567UL;
+static const size_t rcu_stress_test_nthreads = 28;
+static atomic<rcu_stress_test_rec *> rcu_stress_test_array[rcu_stress_test_nthreads];
+static atomic<bool> rcu_stress_test_keep_going(true);
+
+static void
+rcu_stress_test_deleter_fn(void *px)
+{
+  ALWAYS_ASSERT( ((rcu_stress_test_rec *) px)->magic_ == rcu_stress_test_magic );
+  rcu::s_instance.dealloc(px, sizeof(rcu_stress_test_rec));
+}
+
+static void
+rcu_stress_test_worker(unsigned id)
+{
+  rcu::s_instance.pin_current_thread(id);
+  rcu_stress_test_rec *mypx =
+    (rcu_stress_test_rec *) rcu::s_instance.alloc(sizeof(rcu_stress_test_rec));
+  mypx->magic_ = rcu_stress_test_magic;
+  mypx->counter_ = 0;
+  rcu_stress_test_array[id].store(mypx, memory_order_release);
+  while (rcu_stress_test_keep_going.load(memory_order_acquire)) {
+    scoped_rcu_region rcu;
+    for (size_t i = 0; i < rcu_stress_test_nthreads; i++) {
+      rcu_stress_test_rec *p = rcu_stress_test_array[i].load(memory_order_acquire);
+      if (!p)
+        continue;
+      ALWAYS_ASSERT(p->magic_ == rcu_stress_test_magic);
+      p->counter_++; // let it be racy, doesn't matter
+    }
+    // swap it out
+    mypx = rcu_stress_test_array[id].load(memory_order_acquire);
+    if (mypx) {
+      rcu_stress_test_array[id].store(nullptr, memory_order_release);
+      rcu::s_instance.free_with_fn(mypx, rcu_stress_test_deleter_fn);
+    } else {
+      mypx = (rcu_stress_test_rec *)
+        rcu::s_instance.alloc(sizeof(rcu_stress_test_rec));
+      mypx->magic_ = rcu_stress_test_magic;
+      mypx->counter_ = 0;
+      rcu_stress_test_array[id].store(mypx, memory_order_release);
+    }
+  }
+}
+
+static void
+rcu_stress_test()
+{
+  for (size_t i = 0; i < rcu_stress_test_nthreads; i++)
+    rcu_stress_test_array[i].store(nullptr, memory_order_release);
+  vector<thread> workers;
+  for (size_t i = 0; i < rcu_stress_test_nthreads; i++)
+    workers.emplace_back(rcu_stress_test_worker, i);
+  sleep(120);
+  rcu_stress_test_keep_going.store(false, memory_order_release);
+  for (auto &t : workers)
+    t.join();
+  cerr << "rcu stress test completed" << endl;
+}
+
+void
+rcu::Test()
+{
+  rcu_stress_test();
+}
diff --git a/silo/rcu.h b/silo/rcu.h
new file mode 100644 (file)
index 0000000..98db71d
--- /dev/null
@@ -0,0 +1,333 @@
+#ifndef _RCU_H_
+#define _RCU_H_
+
+#include <stdint.h>
+#include <pthread.h>
+
+#include <map>
+#include <vector>
+#include <list>
+#include <utility>
+
+#include "allocator.h"
+#include "counter.h"
+#include "spinlock.h"
+#include "util.h"
+#include "ticker.h"
+#include "pxqueue.h"
+
+class rcu {
+  template <bool> friend class scoped_rcu_base;
+public:
+  class sync;
+  typedef uint64_t epoch_t;
+
+  typedef void (*deleter_t)(void *);
+  struct delete_entry {
+      void* ptr;
+      intptr_t action;
+
+      inline delete_entry(void* ptr, size_t sz)
+          : ptr(ptr), action(-sz) {
+          INVARIANT(action < 0);
+      }
+      inline delete_entry(void* ptr, deleter_t fn)
+          : ptr(ptr), action(reinterpret_cast<uintptr_t>(fn)) {
+          INVARIANT(action > 0);
+      }
+      void run(rcu::sync& s) {
+          if (action < 0)
+              s.dealloc(ptr, -action);
+          else
+              (*reinterpret_cast<deleter_t>(action))(ptr);
+      }
+      bool operator==(const delete_entry& x) const {
+          return ptr == x.ptr && action == x.action;
+      }
+      bool operator!=(const delete_entry& x) const {
+          return !(*this == x);
+      }
+      bool operator<(const delete_entry& x) const {
+          return ptr < x.ptr || (ptr == x.ptr && action < x.action);
+      }
+  };
+  typedef basic_px_queue<delete_entry, 4096> px_queue;
+
+  template <typename T>
+  static inline void
+  deleter(void *p)
+  {
+    delete (T *) p;
+  }
+
+  template <typename T>
+  static inline void
+  deleter_array(void *p)
+  {
+    delete [] (T *) p;
+  }
+
+#ifdef CHECK_INVARIANTS
+  static const uint64_t EpochTimeMultiplier = 10; /* 10 * 1 ms */
+#else
+  static const uint64_t EpochTimeMultiplier = 25; /* 25 * 40 ms */
+#endif
+
+  static_assert(EpochTimeMultiplier >= 1, "XX");
+
+  // legacy helpers
+  static const uint64_t EpochTimeUsec = ticker::tick_us * EpochTimeMultiplier;
+  static const uint64_t EpochTimeNsec = EpochTimeUsec * 1000;
+
+  static const size_t NQueueGroups = 32;
+
+  // all RCU threads interact w/ the RCU subsystem via
+  // a sync struct
+  //
+  // this is also serving as a memory allocator for the time being
+  class sync {
+    friend class rcu;
+    template <bool> friend class scoped_rcu_base;
+  public:
+    px_queue queue_;
+    px_queue scratch_;
+    unsigned depth_; // 0 indicates no rcu region
+    unsigned last_reaped_epoch_;
+#ifdef ENABLE_EVENT_COUNTERS
+    uint64_t last_reaped_timestamp_us_;
+    uint64_t last_release_timestamp_us_;
+#endif
+
+  private:
+    rcu *impl_;
+
+    // local memory allocator
+    ssize_t pin_cpu_;
+    void *arenas_[allocator::MAX_ARENAS];
+    size_t deallocs_[allocator::MAX_ARENAS]; // keeps track of the number of
+                                             // un-released deallocations
+
+  public:
+
+    sync(rcu *impl)
+      : depth_(0)
+      , last_reaped_epoch_(0)
+#ifdef ENABLE_EVENT_COUNTERS
+      , last_reaped_timestamp_us_(0)
+      , last_release_timestamp_us_(0)
+#endif
+      , impl_(impl)
+      , pin_cpu_(-1)
+    {
+      ALWAYS_ASSERT(((uintptr_t)this % CACHELINE_SIZE) == 0);
+      queue_.alloc_freelist(NQueueGroups);
+      scratch_.alloc_freelist(NQueueGroups);
+      NDB_MEMSET(&arenas_[0], 0, sizeof(arenas_));
+      NDB_MEMSET(&deallocs_[0], 0, sizeof(deallocs_));
+    }
+
+    inline void
+    set_pin_cpu(size_t cpu)
+    {
+      pin_cpu_ = cpu;
+    }
+
+    inline ssize_t
+    get_pin_cpu() const
+    {
+      return pin_cpu_;
+    }
+
+    // allocate a block of memory of size sz. caller needs to remember
+    // the size of the allocation when calling free
+    void *alloc(size_t sz);
+
+    // allocates a block of memory of size sz, with the intention of never
+    // free-ing it. is meant for reasonably large allocations (order of pages)
+    void *alloc_static(size_t sz);
+
+    void dealloc(void *p, size_t sz);
+    void dealloc_rcu(void *p, size_t sz);
+
+    // try to release local arenas back to the allocator based on some simple
+    // thresholding heuristics-- is relative expensive operation.  returns true
+    // if a release was actually performed, false otherwise
+    bool try_release();
+
+    void do_cleanup();
+
+    inline unsigned depth() const { return depth_; }
+
+  private:
+
+    void do_release();
+
+    inline void
+    ensure_arena(size_t arena)
+    {
+      if (likely(arenas_[arena]))
+        return;
+      INVARIANT(pin_cpu_ >= 0);
+      arenas_[arena] = allocator::AllocateArenas(pin_cpu_, arena);
+    }
+  };
+
+  // thin forwarders
+  inline void *
+  alloc(size_t sz)
+  {
+    return mysync().alloc(sz);
+  }
+
+  inline void *
+  alloc_static(size_t sz)
+  {
+    return mysync().alloc_static(sz);
+  }
+
+  // this releases memory back to the allocator subsystem
+  // this should NOT be used to free objects!
+  inline void
+  dealloc(void *p, size_t sz)
+  {
+    return mysync().dealloc(p, sz);
+  }
+
+  void dealloc_rcu(void *p, size_t sz);
+
+  inline bool
+  try_release()
+  {
+    return mysync().try_release();
+  }
+
+  inline void
+  do_cleanup()
+  {
+    mysync().do_cleanup();
+  }
+
+  void free_with_fn(void *p, deleter_t fn);
+
+  template <typename T>
+  inline void
+  free(T *p)
+  {
+    free_with_fn(p, deleter<T>);
+  }
+
+  template <typename T>
+  inline void
+  free_array(T *p)
+  {
+    free_with_fn(p, deleter_array<T>);
+  }
+
+  // the tick is in units of rcu ticks
+  inline bool
+  in_rcu_region(uint64_t &rcu_tick) const
+  {
+    const sync *s = syncs_.myview();
+    if (unlikely(!s))
+      return false;
+    const bool is_guarded = ticker::s_instance.is_locally_guarded(rcu_tick);
+    const bool has_depth = s->depth();
+    if (has_depth && !is_guarded)
+      INVARIANT(false);
+    rcu_tick = to_rcu_ticks(rcu_tick);
+    return has_depth;
+  }
+
+  inline bool
+  in_rcu_region() const
+  {
+    uint64_t rcu_tick;
+    return in_rcu_region(rcu_tick);
+  }
+
+  // all threads have moved at least to the cleaning tick, so any pointers <
+  // the cleaning tick can be safely purged
+  inline uint64_t
+  cleaning_rcu_tick_exclusive() const
+  {
+    return to_rcu_ticks(ticker::s_instance.global_last_tick_exclusive());
+  }
+
+  // pin the current thread to CPU.
+  //
+  // this CPU number corresponds to the ones exposed by
+  // sched.h. note that we currently pin to the numa node
+  // associated with the cpu. memory allocation, however, is
+  // CPU-specific
+  void pin_current_thread(size_t cpu);
+
+  void fault_region();
+
+  static rcu s_instance CACHE_ALIGNED; // system wide instance
+
+  static void Test();
+
+private:
+
+  rcu(); // private ctor to enforce singleton
+
+  static inline uint64_t constexpr
+  to_rcu_ticks(uint64_t ticks)
+  {
+    return ticks / EpochTimeMultiplier;
+  }
+
+  inline sync &mysync() { return syncs_.my(this); }
+
+  percore_lazy<sync> syncs_;
+};
+
+template <bool DoCleanup>
+class scoped_rcu_base {
+public:
+
+  // movable, but not copy-constructable
+  scoped_rcu_base(scoped_rcu_base &&) = default;
+  scoped_rcu_base(const scoped_rcu_base &) = delete;
+  scoped_rcu_base &operator=(const scoped_rcu_base &) = delete;
+
+  scoped_rcu_base()
+    : sync_(&rcu::s_instance.mysync()),
+      guard_(ticker::s_instance)
+  {
+    sync_->depth_++;
+  }
+
+  ~scoped_rcu_base()
+  {
+    INVARIANT(sync_->depth_);
+    const unsigned new_depth = --sync_->depth_;
+    guard_.destroy();
+    if (new_depth || !DoCleanup)
+      return;
+    // out of RCU region now, check if we need to run cleaner
+    sync_->do_cleanup();
+  }
+
+  inline ticker::guard *
+  guard()
+  {
+    return guard_.obj();
+  }
+
+  inline rcu::sync *
+  sync()
+  {
+    return sync_;
+  }
+
+private:
+  rcu::sync *sync_;
+  unmanaged<ticker::guard> guard_;
+};
+
+typedef scoped_rcu_base<true> scoped_rcu_region;
+
+class disabled_rcu_region {};
+
+#endif /* _RCU_H_ */
diff --git a/silo/record/cursor.h b/silo/record/cursor.h
new file mode 100644 (file)
index 0000000..3d4938f
--- /dev/null
@@ -0,0 +1,174 @@
+#pragma once
+
+#include <cstdint>
+#include "../macros.h"
+#include "../counter.h"
+#include "../util.h"
+
+// cursors can only move forward, or reset completely
+template <typename T>
+struct read_record_cursor {
+public:
+  typedef typename T::value value_type;
+  typedef typename T::value_descriptor value_descriptor_type;
+
+  // [px, px+nbytes) is assumed to be valid memory
+  read_record_cursor(const uint8_t *px, size_t nbytes)
+    : px_begin(px), nbytes(nbytes), px_cur(px), n(0) {}
+
+  // returns true on success, false on fail
+  inline bool
+  skip_to(size_t i)
+  {
+    INVARIANT(i >= n);
+    INVARIANT(i < value_descriptor_type::nfields());
+    while (n < i) {
+      const size_t sz = value_descriptor_type::failsafe_skip_fn(n++)(px_cur, nbytes, nullptr);
+      if (unlikely(!sz))
+        return false;
+      px_cur += sz;
+      nbytes -= sz;
+    }
+    return true;
+  }
+
+  inline void
+  reset()
+  {
+    INVARIANT(px_cur >= px_begin);
+    nbytes += (px_cur - px_begin);
+    px_cur = px_begin;
+    n = 0;
+  }
+
+  inline size_t
+  field() const
+  {
+    return n;
+  }
+
+  // returns 0 on failure
+  inline size_t
+  read_current_and_advance(value_type *v)
+  {
+    INVARIANT(n < value_descriptor_type::nfields());
+    uint8_t * const buf = reinterpret_cast<uint8_t *>(v) +
+      value_descriptor_type::cstruct_offsetof(n);
+    const uint8_t * const px_skip =
+      value_descriptor_type::failsafe_read_fn(n++)(px_cur, nbytes, buf);
+    if (unlikely(!px_skip))
+      return 0;
+    const size_t rawsz = px_skip - px_cur;
+    INVARIANT(rawsz <= value_descriptor_type::max_nbytes(n - 1));
+    nbytes -= rawsz;
+    px_cur = px_skip;
+    return rawsz;
+  }
+
+  // returns 0 on failure
+  inline size_t
+  read_current_raw_size_and_advance()
+  {
+    INVARIANT(n < value_descriptor_type::nfields());
+    const size_t rawsz =
+      value_descriptor_type::failsafe_skip_fn(n++)(px_cur, nbytes, nullptr);
+    if (unlikely(!nbytes))
+      return 0;
+    INVARIANT(rawsz <= value_descriptor_type::max_nbytes(n - 1));
+    nbytes -= rawsz;
+    px_cur += rawsz;
+    return rawsz;
+  }
+
+private:
+  const uint8_t *px_begin;
+
+  // the 3 fields below are kept in sync:
+  // [px_cur, px_cur+nbytes) is valid memory
+  // px_cur points to the start of the n-th field
+  size_t nbytes;
+  const uint8_t *px_cur;
+  size_t n; // current field position in cursor
+};
+
+
+template <typename T>
+struct write_record_cursor {
+public:
+  typedef typename T::value value_type;
+  typedef typename T::value_descriptor value_descriptor_type;
+
+  write_record_cursor(uint8_t *px)
+    : px_begin(px), px_cur(px), px_end(nullptr), n(0)
+  {
+    INVARIANT(px);
+  }
+
+  inline void
+  skip_to(size_t i)
+  {
+    INVARIANT(i >= n);
+    INVARIANT(i < value_descriptor_type::nfields());
+    while (n < i)
+      px_cur += value_descriptor_type::skip_fn(n++)(px_cur, nullptr);
+  }
+
+  inline void
+  reset()
+  {
+    px_cur = px_begin;
+    n = 0;
+  }
+
+  inline size_t
+  field() const
+  {
+    return n;
+  }
+
+  inline void
+  write_current_and_advance(const value_type *v, uint8_t *old_v = nullptr)
+  {
+    static event_counter evt_write_memmove(
+        util::cxx_typename<T>::value() + std::string("_write_memmove"));
+    const uint8_t * const buf = reinterpret_cast<const uint8_t *>(v) +
+      value_descriptor_type::cstruct_offsetof(n);
+    const size_t newsz = value_descriptor_type::nbytes_fn(n)(buf);
+    INVARIANT(newsz <= value_descriptor_type::max_nbytes(n));
+    uint8_t stack_buf[value_descriptor_type::max_nbytes(n)];
+    uint8_t * const old_buf = old_v ? old_v : &stack_buf[0];
+    const size_t oldsz = value_descriptor_type::skip_fn(n)(px_cur, old_buf);
+    INVARIANT(oldsz <= value_descriptor_type::max_nbytes(n));
+    if (unlikely(oldsz != newsz)) {
+      ++evt_write_memmove;
+      compute_end();
+      memmove(px_cur + newsz, px_cur + oldsz, px_end - px_cur - oldsz);
+      if (oldsz > newsz)
+        // shrink
+        px_end -= (oldsz - newsz);
+      else
+        // grow
+        px_end += (newsz - oldsz);
+    }
+    px_cur = value_descriptor_type::write_fn(n++)(px_cur, buf);
+  }
+
+private:
+
+  inline void
+  compute_end()
+  {
+    if (px_end)
+      return;
+    uint8_t *px = px_cur;
+    size_t i = n;
+    while (i < value_descriptor_type::nfields())
+      px += value_descriptor_type::skip_fn(i++)(px, nullptr);
+    px_end = px;
+  }
+
+  uint8_t *px_begin;
+  uint8_t *px_cur;
+  uint8_t *px_end;
+  size_t n; // current field position in cursor
+};
diff --git a/silo/record/encoder.h b/silo/record/encoder.h
new file mode 100644 (file)
index 0000000..8a678e8
--- /dev/null
@@ -0,0 +1,625 @@
+#ifndef _NDB_BENCH_ENCODER_H_
+#define _NDB_BENCH_ENCODER_H_
+
+#include <string>
+#include <stdint.h>
+#include "serializer.h"
+#include "../util.h"
+#include "../ndb_type_traits.h"
+
+#if NDB_MASSTREE
+#include "../masstree/str.hh"
+#endif
+
+// the C preprocessor is absolutely wonderful...
+
+template <typename T> struct encoder {};
+
+template <typename T>
+static inline std::string
+Encode(const T &t)
+{
+  const encoder<T> enc;
+  return enc.write(&t);
+}
+
+template <typename T>
+static inline const char *
+Encode(uint8_t *buf, const T &t)
+{
+  const encoder<T> enc;
+  return (const char *) enc.write(buf, &t);
+}
+
+template <typename T>
+static inline std::string &
+Encode(std::string &buf, const T &t)
+{
+  const encoder<T> enc;
+  return enc.write(buf, &t);
+}
+
+template <typename T>
+static inline const T *
+Decode(const std::string &buf, T &obj)
+{
+  const encoder<T> enc;
+  return enc.read(buf.data(), &obj);
+}
+
+template <typename T>
+static inline const T *
+Decode(const char *buf, T &obj)
+{
+  const encoder<T> enc;
+  return enc.read(buf, &obj);
+}
+
+template <typename T>
+static inline const T *
+PrefixDecode(const std::string &buf, T &obj, size_t prefix)
+{
+  const encoder<T> enc;
+  return enc.prefix_read(buf.data(), &obj, prefix);
+}
+
+template <typename T>
+static inline const T *
+PrefixDecode(const char *buf, T &obj, size_t prefix)
+{
+  const encoder<T> enc;
+  return enc.read(buf, &obj, prefix);
+}
+
+template <typename T>
+static inline size_t
+Size(const T &t)
+{
+  const encoder<T> enc;
+  return enc.nbytes(&t);
+}
+
+#define IDENT_TRANSFORM(tpe, expr) (expr)
+#define HOST_TO_BIG_TRANSFORM(tpe, expr) (util::host_endian_trfm< tpe >()(expr))
+#define BIG_TO_HOST_TRANSFORM(tpe, expr) (util::big_endian_trfm< tpe >()(expr))
+
+#define STRUCT_LAYOUT_X(tpe, name) \
+  tpe name;
+
+#define STRUCT_EQ_X(tpe, name) \
+  if (this->name != other.name) \
+    return false;
+
+#define STRUCT_PARAM_FIRST_X(tpe, name) \
+  typename private_::typeutil< tpe >::func_param_type name
+
+#define STRUCT_PARAM_REST_X(tpe, name) \
+  , typename private_::typeutil< tpe >::func_param_type name
+
+#define STRUCT_INITLIST_FIRST_X(tpe, name) \
+  name(name)
+
+#define STRUCT_INITLIST_REST_X(tpe, name) \
+  , name(name)
+
+#define STRUCT_PRINTER_FIRST_X(tpe, name) \
+  #name << "=" << obj.name
+
+#define STRUCT_PRINTER_REST_X(tpe, name) \
+  << ", " << #name << "=" << obj.name
+
+#define STRUCT_FIELDPOS_X(tpe, name) \
+  name ## _field,
+
+#define SERIALIZE_WRITE_FIELD(tpe, name, compress, trfm) \
+  do { \
+    buf = serializer< tpe, compress >::write(buf, trfm(tpe, obj->name)); \
+  } while (0);
+
+#define SERIALIZE_READ_FIELD(tpe, name, compress, trfm) \
+  do { \
+    buf = serializer< tpe, compress >::read(buf, &obj->name); \
+    obj->name = trfm(tpe, obj->name); \
+  } while (0);
+
+#define SERIALIZE_PREFIX_READ_FIELD(tpe, name, compress, trfm) \
+  do { \
+    buf = serializer< tpe, compress >::read(buf, &obj->name); \
+    obj->name = trfm(tpe, obj->name); \
+    if (++i >= prefix) \
+      return; \
+  } while (0);
+
+#define SERIALIZE_FAILSAFE_READ_FIELD(tpe, name, compress, trfm) \
+  do { \
+    const uint8_t * const p = \
+      serializer< tpe, compress >::failsafe_read(buf, nbytes, &obj->name); \
+    if (unlikely(!p)) \
+      return false; \
+    nbytes -= (p - buf); \
+    buf = p; \
+    obj->name = trfm(tpe, obj->name); \
+  } while (0);
+
+#define SERIALIZE_NBYTES_FIELD(tpe, name, compress) \
+  do { \
+    size += serializer< tpe, compress >::nbytes(&obj->name); \
+  } while (0);
+
+#define SERIALIZE_MAX_NBYTES_KEY_FIELD_X(tpe, name) \
+  serializer< tpe, false >::max_nbytes()
+#define SERIALIZE_MAX_NBYTES_KEY_FIELD_Y(tpe, name) \
+  + serializer< tpe, false >::max_nbytes()
+
+#define SERIALIZE_MAX_NBYTES_VALUE_FIELD_X(tpe, name) \
+  serializer< tpe, true >::max_nbytes()
+#define SERIALIZE_MAX_NBYTES_VALUE_FIELD_Y(tpe, name) \
+  + serializer< tpe, true >::max_nbytes()
+
+#define SERIALIZE_MAX_NBYTES_PREFIX_KEY_FIELD_X(tpe, name) \
+  do { \
+    ret += serializer< tpe, false >::max_nbytes(); \
+    if (++i >= nfields) \
+      return ret; \
+  } while (0);
+
+#define SERIALIZE_MAX_NBYTES_PREFIX_VALUE_FIELD_X(tpe, name) \
+  do { \
+    ret += serializer< tpe, true >::max_nbytes(); \
+    if (++i >= nfields) \
+      return ret; \
+  } while (0);
+
+#define SERIALIZE_WRITE_KEY_FIELD_X(tpe, name) \
+  SERIALIZE_WRITE_FIELD(tpe, name, false, HOST_TO_BIG_TRANSFORM)
+#define SERIALIZE_WRITE_VALUE_FIELD_X(tpe, name) \
+  SERIALIZE_WRITE_FIELD(tpe, name, true, IDENT_TRANSFORM)
+
+#define SERIALIZE_READ_KEY_FIELD_X(tpe, name) \
+  SERIALIZE_READ_FIELD(tpe, name, false, BIG_TO_HOST_TRANSFORM)
+#define SERIALIZE_READ_VALUE_FIELD_X(tpe, name) \
+  SERIALIZE_READ_FIELD(tpe, name, true, IDENT_TRANSFORM)
+
+#define SERIALIZE_PREFIX_READ_KEY_FIELD_X(tpe, name) \
+  SERIALIZE_PREFIX_READ_FIELD(tpe, name, false, BIG_TO_HOST_TRANSFORM)
+#define SERIALIZE_PREFIX_READ_VALUE_FIELD_X(tpe, name) \
+  SERIALIZE_PREFIX_READ_FIELD(tpe, name, true, IDENT_TRANSFORM)
+
+#define SERIALIZE_FAILSAFE_READ_KEY_FIELD_X(tpe, name) \
+  SERIALIZE_FAILSAFE_READ_FIELD(tpe, name, false, BIG_TO_HOST_TRANSFORM)
+#define SERIALIZE_FAILSAFE_READ_VALUE_FIELD_X(tpe, name) \
+  SERIALIZE_FAILSAFE_READ_FIELD(tpe, name, true, IDENT_TRANSFORM)
+
+#define SERIALIZE_NBYTES_KEY_FIELD_X(tpe, name) \
+  SERIALIZE_NBYTES_FIELD(tpe, name, false)
+#define SERIALIZE_NBYTES_VALUE_FIELD_X(tpe, name) \
+  SERIALIZE_NBYTES_FIELD(tpe, name, true)
+
+#define DESCRIPTOR_VALUE_WRITE_FN_X(tpe, name) \
+  &generic_serializer< serializer< tpe, true > >::write,
+#define DESCRIPTOR_VALUE_READ_FN_X(tpe, name) \
+  &generic_serializer< serializer< tpe, true > >::read,
+#define DESCRIPTOR_VALUE_FAILSAFE_READ_FN_X(tpe, name) \
+  &generic_serializer< serializer< tpe, true > >::failsafe_read,
+#define DESCRIPTOR_VALUE_NBYTES_FN_X(tpe, name) \
+  &generic_serializer< serializer< tpe, true > >::nbytes,
+#define DESCRIPTOR_VALUE_SKIP_FN_X(tpe, name) \
+  &generic_serializer< serializer< tpe, true > >::skip,
+#define DESCRIPTOR_VALUE_FAILSAFE_SKIP_FN_X(tpe, name) \
+  &generic_serializer< serializer< tpe, true > >::failsafe_skip,
+#define DESCRIPTOR_VALUE_MAX_NBYTES_X(tpe, name) \
+  serializer< tpe, true >::max_nbytes(),
+#define DESCRIPTOR_VALUE_OFFSETOF_X(tpe, name) \
+  offsetof(value, name),
+#define DESCRIPTOR_VALUE_SIZEOF_X(tpe, name) \
+  sizeof(tpe),
+
+// semantics:
+
+// Write the encoded version of obj into buf, returning a const pointer
+// to buf. Assumes that [buf, buf + nbytes(obj)) is valid memory
+//
+// const uint8_t *
+// write(uint8_t *buf, const T *obj)
+
+// Write the encoded version of obj into buf, returning a const reference
+// to buf. Makes no assumptions about buf
+//
+// std::string &
+// write(std::string &buf, const T *obj)
+
+// Read a serialized, encoded version from buf into obj, returning
+// a const pointer to obj. Assumes that buf points to a valid encoding
+//
+// const T *
+// read(const uint8_t *buf, T *obj)
+
+// Returns the number of bytes required to encode this specific instance
+// of obj.
+//
+// size_t
+// nbytes(const T *obj)
+
+// implements encoded versions of the above functions
+#define DO_STRUCT_ENCODE_REST(name) \
+  inline ALWAYS_INLINE const uint8_t * \
+  write(uint8_t *buf, const struct name *obj) const \
+  { \
+    encode_write(buf, obj); \
+    return buf; \
+  } \
+  inline ALWAYS_INLINE const struct name * \
+  read(const uint8_t *buf, struct name *obj) const \
+  { \
+    encode_read(buf, obj); \
+    return obj; \
+  } \
+  inline ALWAYS_INLINE const struct name * \
+  prefix_read(const uint8_t *buf, struct name *obj, size_t prefix) const \
+  { \
+    encode_prefix_read(buf, obj, prefix); \
+    return obj; \
+  } \
+  inline ALWAYS_INLINE const struct name * \
+  failsafe_read(const uint8_t *buf, size_t nbytes, struct name *obj) const \
+  { \
+    if (unlikely(!encode_failsafe_read(buf, nbytes, obj))) \
+      return nullptr; \
+    return obj; \
+  } \
+  inline ALWAYS_INLINE size_t \
+  nbytes(const struct name *obj) const \
+  { \
+    return encode_nbytes(obj); \
+  }
+
+// implements direct pass-through version of the above functions
+#define DO_STRUCT_PASS_THROUGH_REST(name) \
+  inline ALWAYS_INLINE const uint8_t * \
+  write(uint8_t *buf, const struct name *obj) const \
+  { \
+    *((struct name *) buf) = *obj; \
+    return buf; \
+  } \
+  inline ALWAYS_INLINE const struct name * \
+  read(const uint8_t *buf, struct name *obj) const \
+  { \
+    *obj = *((const struct name *) buf); \
+    return obj; \
+  } \
+  inline ALWAYS_INLINE const struct name * \
+  prefix_read(const uint8_t *buf, struct name *obj, size_t prefix) const \
+  { \
+    *obj = *((const struct name *) buf); \
+    return obj; \
+  } \
+  inline ALWAYS_INLINE const struct name * \
+  failsafe_read(const uint8_t *buf, size_t nbytes, struct name *obj) const \
+  { \
+    if (unlikely(nbytes < sizeof(*obj))) \
+      return nullptr; \
+    *obj = *((const struct name *) buf); \
+    return obj; \
+  } \
+  inline ALWAYS_INLINE size_t \
+  nbytes(const struct name *obj) const \
+  { \
+    return sizeof(*obj); \
+  }
+
+#if NDB_MASSTREE
+#define DO_STRUCT_MASSTREE(name) \
+  inline ALWAYS_INLINE const struct name * \
+  read(lcdf::Str buf, struct name *obj) const       \
+  { \
+    return read((const uint8_t *) buf.data(), obj); \
+  }
+#else
+#define DO_STRUCT_MASSTREE(name)
+#endif
+
+#define DO_STRUCT_COMMON(name) \
+  inline std::string & \
+  write(std::string &buf, const struct name *obj) const \
+  { \
+    buf.clear(); \
+    buf.resize(nbytes(obj)); \
+    write((uint8_t *) buf.data(), obj); \
+    return buf; \
+  } \
+  inline std::string \
+  write(const struct name *obj) const \
+  { \
+    std::string ret; \
+    write(ret, obj); \
+    return ret; \
+  } \
+  inline ALWAYS_INLINE const struct name * \
+  read(const std::string &buf, struct name *obj) const \
+  { \
+    return read((const uint8_t *) buf.data(), obj); \
+  } \
+  DO_STRUCT_MASSTREE(name) \
+  inline ALWAYS_INLINE const struct name * \
+  read(const char *buf, struct name *obj) const \
+  { \
+    return read((const uint8_t *) buf, obj); \
+  } \
+  inline ALWAYS_INLINE const struct name * \
+  prefix_read(const std::string &buf, struct name *obj, size_t prefix) const \
+  { \
+    return prefix_read((const uint8_t *) buf.data(), obj, prefix); \
+  } \
+  inline ALWAYS_INLINE const struct name * \
+  prefix_read(const char *buf, struct name *obj, size_t prefix) const \
+  { \
+    return prefix_read((const uint8_t *) buf, obj, prefix); \
+  }
+
+#ifdef USE_VARINT_ENCODING
+#define DO_STRUCT_REST_VALUE(name) DO_STRUCT_ENCODE_REST(name)
+#else
+#define DO_STRUCT_REST_VALUE(name) DO_STRUCT_PASS_THROUGH_REST(name)
+#endif
+
+#define APPLY_X_AND_Y(x, y) x(y, y)
+
+// the main macro
+#define DO_STRUCT(name, keyfields, valuefields) \
+  struct name { \
+  struct key { \
+    inline key() {} \
+    inline key(keyfields(STRUCT_PARAM_FIRST_X, STRUCT_PARAM_REST_X)) : \
+      keyfields(STRUCT_INITLIST_FIRST_X, STRUCT_INITLIST_REST_X) {} \
+    APPLY_X_AND_Y(keyfields, STRUCT_LAYOUT_X) \
+    inline bool \
+    operator==(const struct key &other) const \
+    { \
+      APPLY_X_AND_Y(keyfields, STRUCT_EQ_X) \
+      return true; \
+    } \
+    inline bool \
+    operator!=(const struct key &other) const \
+    { \
+      return !operator==(other); \
+    } \
+    enum { \
+      APPLY_X_AND_Y(keyfields, STRUCT_FIELDPOS_X) \
+      NFIELDS \
+    }; \
+  } PACKED; \
+  struct value { \
+    inline value() {} \
+    inline value(valuefields(STRUCT_PARAM_FIRST_X, STRUCT_PARAM_REST_X)) : \
+      valuefields(STRUCT_INITLIST_FIRST_X, STRUCT_INITLIST_REST_X) {} \
+    APPLY_X_AND_Y(valuefields, STRUCT_LAYOUT_X) \
+    inline bool \
+    operator==(const struct value &other) const \
+    { \
+      APPLY_X_AND_Y(valuefields, STRUCT_EQ_X) \
+      return true; \
+    } \
+    inline bool \
+    operator!=(const struct value &other) const \
+    { \
+      return !operator==(other); \
+    } \
+    enum { \
+      APPLY_X_AND_Y(valuefields, STRUCT_FIELDPOS_X) \
+      NFIELDS \
+    }; \
+  } PACKED; \
+  struct value_descriptor { \
+    static inline generic_write_fn \
+    write_fn(size_t i) \
+    { \
+      static generic_write_fn write_fns[] = { \
+        APPLY_X_AND_Y(valuefields, DESCRIPTOR_VALUE_WRITE_FN_X) \
+      }; \
+      return write_fns[i]; \
+    } \
+    static inline generic_read_fn \
+    read_fn(size_t i) \
+    { \
+      static generic_read_fn read_fns[] = { \
+        APPLY_X_AND_Y(valuefields, DESCRIPTOR_VALUE_READ_FN_X) \
+      }; \
+      return read_fns[i]; \
+    } \
+    static inline generic_failsafe_read_fn \
+    failsafe_read_fn(size_t i) \
+    { \
+      static generic_failsafe_read_fn failsafe_read_fns[] = { \
+        APPLY_X_AND_Y(valuefields, DESCRIPTOR_VALUE_FAILSAFE_READ_FN_X) \
+      }; \
+      return failsafe_read_fns[i]; \
+    } \
+    static inline generic_nbytes_fn \
+    nbytes_fn(size_t i) \
+    { \
+      static generic_nbytes_fn nbytes_fns[] = { \
+        APPLY_X_AND_Y(valuefields, DESCRIPTOR_VALUE_NBYTES_FN_X) \
+      }; \
+      return nbytes_fns[i]; \
+    } \
+    static inline generic_skip_fn \
+    skip_fn(size_t i) \
+    { \
+      static generic_skip_fn skip_fns[] = { \
+        APPLY_X_AND_Y(valuefields, DESCRIPTOR_VALUE_SKIP_FN_X) \
+      }; \
+      return skip_fns[i]; \
+    } \
+    static inline generic_failsafe_skip_fn \
+    failsafe_skip_fn(size_t i) \
+    { \
+      static generic_failsafe_skip_fn failsafe_skip_fns[] = { \
+        APPLY_X_AND_Y(valuefields, DESCRIPTOR_VALUE_FAILSAFE_SKIP_FN_X) \
+      }; \
+      return failsafe_skip_fns[i]; \
+    } \
+    static inline constexpr size_t \
+    nfields() \
+    { \
+      return static_cast<size_t>(value::NFIELDS); \
+    } \
+    static inline size_t \
+    max_nbytes(size_t i) \
+    { \
+      static size_t maxn[] = { \
+        APPLY_X_AND_Y(valuefields, DESCRIPTOR_VALUE_MAX_NBYTES_X) \
+      }; \
+      return maxn[i]; \
+    } \
+    static inline size_t \
+    cstruct_offsetof(size_t i) \
+    { \
+      static size_t offsets[] = { \
+        APPLY_X_AND_Y(valuefields, DESCRIPTOR_VALUE_OFFSETOF_X) \
+      }; \
+      return offsets[i]; \
+    } \
+    static inline size_t \
+    cstruct_sizeof(size_t i) \
+    { \
+      static size_t sizeofs[] = { \
+        APPLY_X_AND_Y(valuefields, DESCRIPTOR_VALUE_SIZEOF_X) \
+      }; \
+      return sizeofs[i]; \
+    } \
+  }; \
+  }; \
+  inline std::ostream & \
+  operator<<(std::ostream &o, const name::key &obj) \
+  { \
+    o << "{" << keyfields(STRUCT_PRINTER_FIRST_X, STRUCT_PRINTER_REST_X) << "}"; \
+    return o; \
+  } \
+  inline std::ostream & \
+  operator<<(std::ostream &o, const name::value &obj) \
+  { \
+    o << "{" << valuefields(STRUCT_PRINTER_FIRST_X, STRUCT_PRINTER_REST_X) << "}"; \
+    return o; \
+  } \
+  namespace private_ { \
+  template <> \
+  struct is_trivially_destructible< name::key > { \
+    static const bool value = true; \
+  }; \
+  } \
+  template <> \
+  struct encoder< name::key > { \
+  inline void \
+  encode_write(uint8_t *buf, const struct name::key *obj) const \
+  { \
+    APPLY_X_AND_Y(keyfields, SERIALIZE_WRITE_KEY_FIELD_X) \
+  } \
+  inline void \
+  encode_read(const uint8_t *buf, struct name::key *obj) const \
+  { \
+    APPLY_X_AND_Y(keyfields, SERIALIZE_READ_KEY_FIELD_X) \
+  } \
+  inline void \
+  encode_prefix_read(const uint8_t *buf, struct name::key *obj, size_t prefix) const \
+  { \
+    size_t i = 0; \
+    APPLY_X_AND_Y(keyfields, SERIALIZE_PREFIX_READ_KEY_FIELD_X) \
+  } \
+  inline bool \
+  encode_failsafe_read(const uint8_t *buf, size_t nbytes, struct name::key *obj) const \
+  { \
+    APPLY_X_AND_Y(keyfields, SERIALIZE_FAILSAFE_READ_KEY_FIELD_X) \
+    return true; \
+  } \
+  inline ALWAYS_INLINE size_t \
+  encode_nbytes(const struct name::key *obj) const \
+  { \
+    return sizeof(*obj); \
+  } \
+  static inline constexpr size_t \
+  encode_max_nbytes() \
+  { \
+    return keyfields(SERIALIZE_MAX_NBYTES_KEY_FIELD_X, \
+                     SERIALIZE_MAX_NBYTES_KEY_FIELD_Y); \
+  } \
+  inline ALWAYS_INLINE size_t \
+  encode_max_nbytes_prefix(size_t nfields) const \
+  { \
+    size_t ret = 0; \
+    size_t i = 0; \
+    if (likely(nfields >= name::key::NFIELDS)) \
+      return std::numeric_limits<size_t>::max(); \
+    APPLY_X_AND_Y(keyfields, SERIALIZE_MAX_NBYTES_PREFIX_KEY_FIELD_X) \
+    return ret; \
+  } \
+  DO_STRUCT_COMMON(name::key) \
+  DO_STRUCT_ENCODE_REST(name::key) \
+  }; \
+  namespace private_ { \
+  template <> \
+  struct is_trivially_destructible< name::value > { \
+    static const bool value = true; \
+  }; \
+  } \
+  template <> \
+  struct encoder< name::value > { \
+  inline void \
+  encode_write(uint8_t *buf, const struct name::value *obj) const \
+  { \
+    APPLY_X_AND_Y(valuefields, SERIALIZE_WRITE_VALUE_FIELD_X) \
+  } \
+  inline void \
+  encode_read(const uint8_t *buf, struct name::value *obj) const \
+  { \
+    APPLY_X_AND_Y(valuefields, SERIALIZE_READ_VALUE_FIELD_X) \
+  } \
+  inline void \
+  encode_prefix_read(const uint8_t *buf, struct name::value *obj, size_t prefix) const \
+  { \
+    size_t i = 0; \
+    APPLY_X_AND_Y(valuefields, SERIALIZE_PREFIX_READ_VALUE_FIELD_X) \
+  } \
+  inline bool \
+  encode_failsafe_read(const uint8_t *buf, size_t nbytes, struct name::value *obj) const \
+  { \
+    APPLY_X_AND_Y(valuefields, SERIALIZE_FAILSAFE_READ_VALUE_FIELD_X) \
+    return true; \
+  } \
+  inline size_t \
+  encode_nbytes(const struct name::value *obj) const \
+  { \
+    size_t size = 0; \
+    APPLY_X_AND_Y(valuefields, SERIALIZE_NBYTES_VALUE_FIELD_X) \
+    return size; \
+  } \
+  static inline constexpr size_t \
+  encode_max_nbytes() \
+  { \
+    return valuefields(SERIALIZE_MAX_NBYTES_VALUE_FIELD_X, \
+                       SERIALIZE_MAX_NBYTES_VALUE_FIELD_Y); \
+  } \
+  inline ALWAYS_INLINE size_t \
+  encode_max_nbytes_prefix(size_t nfields) const \
+  { \
+    size_t ret = 0; \
+    size_t i = 0; \
+    if (likely(nfields >= name::value::NFIELDS)) \
+      return std::numeric_limits<size_t>::max(); \
+    APPLY_X_AND_Y(valuefields, SERIALIZE_MAX_NBYTES_PREFIX_VALUE_FIELD_X) \
+    return ret; \
+  } \
+  DO_STRUCT_COMMON(name::value) \
+  DO_STRUCT_REST_VALUE(name::value) \
+  };
+
+template <typename T>
+struct schema {
+  typedef T base_type;
+  typedef typename T::key key_type;
+  typedef typename T::value value_type;
+  typedef typename T::value_descriptor value_descriptor_type;
+  typedef encoder<key_type> key_encoder_type;
+  typedef encoder<value_type> value_encoder_type;
+};
+
+#endif /* _NDB_BENCH_ENCODER_H_ */
diff --git a/silo/record/inline_str.h b/silo/record/inline_str.h
new file mode 100644 (file)
index 0000000..c44cb5f
--- /dev/null
@@ -0,0 +1,352 @@
+#ifndef _NDB_BENCH_INLINE_STR_H_
+#define _NDB_BENCH_INLINE_STR_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+#include <ostream>
+
+#include "../macros.h"
+#include "serializer.h"
+
+// equivalent to VARCHAR(N)
+
+template <typename IntSizeType, unsigned int N>
+class inline_str_base {
+  // XXX: argh...
+  template <typename T, bool DoCompress> friend class serializer;
+public:
+
+  inline_str_base() : sz(0) {}
+
+  inline_str_base(const char *s)
+  {
+    assign(s);
+  }
+
+  inline_str_base(const char *s, size_t n)
+  {
+    assign(s, n);
+  }
+
+  inline_str_base(const std::string &s)
+  {
+    assign(s);
+  }
+
+  inline_str_base(const inline_str_base &that)
+    : sz(that.sz)
+  {
+    NDB_MEMCPY(&buf[0], &that.buf[0], sz);
+  }
+
+  inline_str_base &
+  operator=(const inline_str_base &that)
+  {
+    if (this == &that)
+      return *this;
+    sz = that.sz;
+    NDB_MEMCPY(&buf[0], &that.buf[0], sz);
+    return *this;
+  }
+
+  inline size_t
+  max_size() const
+  {
+    return N;
+  }
+
+  inline const char *
+  c_str() const
+  {
+    buf[sz] = 0;
+    return &buf[0];
+  }
+
+  inline std::string
+  str(bool zeropad = false) const
+  {
+               if (zeropad) {
+                       INVARIANT(N >= sz);
+                       std::string r(N, 0);
+                       NDB_MEMCPY((char *) r.data(), &buf[0], sz);
+                       return r;
+               } else {
+                       return std::string(&buf[0], sz);
+               }
+  }
+
+  inline ALWAYS_INLINE const char *
+  data() const
+  {
+    return &buf[0];
+  }
+
+  inline ALWAYS_INLINE size_t
+  size() const
+  {
+    return sz;
+  }
+
+  inline ALWAYS_INLINE void
+  assign(const char *s)
+  {
+    assign(s, strlen(s));
+  }
+
+  inline void
+  assign(const char *s, size_t n)
+  {
+    INVARIANT(n <= N);
+    NDB_MEMCPY(&buf[0], s, n);
+    sz = n;
+  }
+
+  inline ALWAYS_INLINE void
+  assign(const std::string &s)
+  {
+    assign(s.data(), s.size());
+  }
+
+  inline void
+  resize(size_t n, char c = 0)
+  {
+    INVARIANT(n <= N);
+    if (n > sz)
+      NDB_MEMSET(&buf[sz], c, n - sz);
+    sz = n;
+  }
+
+  inline void
+  resize_junk(size_t n)
+  {
+    INVARIANT(n <= N);
+    sz = n;
+  }
+
+  inline bool
+  operator==(const inline_str_base &other) const
+  {
+    return memcmp(buf, other.buf, sz) == 0;
+  }
+
+  inline bool
+  operator!=(const inline_str_base &other) const
+  {
+    return !operator==(other);
+  }
+
+private:
+  IntSizeType sz;
+  mutable char buf[N + 1];
+} PACKED;
+
+template <typename IntSizeType, unsigned int N>
+inline std::ostream &
+operator<<(std::ostream &o, const inline_str_base<IntSizeType, N> &s)
+{
+  o << std::string(s.data(), s.size());
+  return o;
+}
+
+template <unsigned int N>
+class inline_str_8 : public inline_str_base<uint8_t, N> {
+  typedef inline_str_base<uint8_t, N> super_type;
+public:
+  inline_str_8() : super_type() {}
+  inline_str_8(const char *s) : super_type(s) {}
+  inline_str_8(const char *s, size_t n) : super_type(s, n) {}
+  inline_str_8(const std::string &s) : super_type(s) {}
+} PACKED;
+
+template <unsigned int N>
+class inline_str_16 : public inline_str_base<uint16_t, N> {
+  typedef inline_str_base<uint16_t, N> super_type;
+public:
+  inline_str_16() : super_type() {}
+  inline_str_16(const char *s) : super_type(s) {}
+  inline_str_16(const char *s, size_t n) : super_type(s, n) {}
+  inline_str_16(const std::string &s) : super_type(s) {}
+} PACKED;
+
+// equiavlent to CHAR(N)
+template <unsigned int N, char FillChar = ' '>
+class inline_str_fixed {
+  // XXX: argh...
+  template <typename T, bool DoCompress> friend class serializer;
+public:
+  inline_str_fixed()
+  {
+    NDB_MEMSET(&buf[0], FillChar, N);
+  }
+
+  inline_str_fixed(const char *s)
+  {
+    assign(s, strlen(s));
+  }
+
+  inline_str_fixed(const char *s, size_t n)
+  {
+    assign(s, n);
+  }
+
+  inline_str_fixed(const std::string &s)
+  {
+    assign(s.data(), s.size());
+  }
+
+  inline_str_fixed(const inline_str_fixed &that)
+  {
+    NDB_MEMCPY(&buf[0], &that.buf[0], N);
+  }
+
+  inline_str_fixed &
+  operator=(const inline_str_fixed &that)
+  {
+    if (this == &that)
+      return *this;
+    NDB_MEMCPY(&buf[0], &that.buf[0], N);
+    return *this;
+  }
+
+  inline ALWAYS_INLINE std::string
+  str() const
+  {
+    return std::string(&buf[0], N);
+  }
+
+  inline ALWAYS_INLINE const char *
+  data() const
+  {
+    return &buf[0];
+  }
+
+  inline ALWAYS_INLINE size_t
+  size() const
+  {
+    return N;
+  }
+
+  inline ALWAYS_INLINE void
+  assign(const char *s)
+  {
+    assign(s, strlen(s));
+  }
+
+  inline void
+  assign(const char *s, size_t n)
+  {
+    INVARIANT(n <= N);
+    NDB_MEMCPY(&buf[0], s, n);
+    if ((N - n) > 0) // to suppress compiler warning
+      NDB_MEMSET(&buf[n], FillChar, N - n); // pad with spaces
+  }
+
+  inline ALWAYS_INLINE void
+  assign(const std::string &s)
+  {
+    assign(s.data(), s.size());
+  }
+
+  inline bool
+  operator==(const inline_str_fixed &other) const
+  {
+    return memcmp(buf, other.buf, N) == 0;
+  }
+
+  inline bool
+  operator!=(const inline_str_fixed &other) const
+  {
+    return !operator==(other);
+  }
+
+private:
+  char buf[N];
+} PACKED;
+
+template <unsigned int N, char FillChar>
+inline std::ostream &
+operator<<(std::ostream &o, const inline_str_fixed<N, FillChar> &s)
+{
+  o << std::string(s.data(), s.size());
+  return o;
+}
+
+// serializer<T> specialization
+template <typename IntSizeType, unsigned int N, bool Compress>
+struct serializer< inline_str_base<IntSizeType, N>, Compress > {
+  typedef inline_str_base<IntSizeType, N> obj_type;
+  static inline uint8_t *
+  write(uint8_t *buf, const obj_type &obj)
+  {
+    buf = serializer<IntSizeType, Compress>::write(buf, &obj.sz);
+    NDB_MEMCPY(buf, &obj.buf[0], obj.sz);
+    return buf + obj.sz;
+  }
+
+  static const uint8_t *
+  read(const uint8_t *buf, obj_type *obj)
+  {
+    buf = serializer<IntSizeType, Compress>::read(buf, &obj->sz);
+    NDB_MEMCPY(&obj->buf[0], buf, obj->sz);
+    return buf + obj->sz;
+  }
+
+  static const uint8_t *
+  failsafe_read(const uint8_t *buf, size_t nbytes, obj_type *obj)
+  {
+    const uint8_t * const hdrbuf =
+      serializer<IntSizeType, Compress>::failsafe_read(buf, nbytes, &obj->sz);
+    if (unlikely(!hdrbuf))
+      return nullptr;
+    nbytes -= (hdrbuf - buf);
+    if (nbytes < obj->sz)
+      return nullptr;
+    buf = hdrbuf;
+    NDB_MEMCPY(&obj->buf[0], buf, obj->sz);
+    return buf + obj->sz;
+  }
+
+  static inline size_t
+  nbytes(const obj_type *obj)
+  {
+    return serializer<IntSizeType, Compress>::nbytes(&obj->sz) + obj->sz;
+  }
+
+  static inline size_t
+  skip(const uint8_t *stream, uint8_t *oldv)
+  {
+    IntSizeType sz = 0;
+    const uint8_t * const body = serializer<IntSizeType, Compress>::read(stream, &sz);
+    const size_t totalsz = (body - stream) + sz;
+    if (oldv)
+      NDB_MEMCPY(oldv, stream, totalsz);
+    return totalsz;
+  }
+
+  static inline size_t
+  failsafe_skip(const uint8_t *stream, size_t nbytes, uint8_t *oldv)
+  {
+    IntSizeType sz = 0;
+    const uint8_t * const body =
+      serializer<IntSizeType, Compress>::failsafe_read(stream, nbytes, &sz);
+    if (unlikely(!body))
+      return 0;
+    nbytes -= (body - stream);
+    if (unlikely(nbytes < sz))
+      return 0;
+    const size_t totalsz = (body - stream) + sz;
+    if (oldv)
+      NDB_MEMCPY(oldv, stream, totalsz);
+    return totalsz;
+  }
+
+  static inline constexpr size_t
+  max_nbytes()
+  {
+    return serializer<IntSizeType, Compress>::max_bytes() + N;
+  }
+};
+
+#endif /* _NDB_BENCH_INLINE_STR_H_ */
diff --git a/silo/record/serializer.h b/silo/record/serializer.h
new file mode 100644 (file)
index 0000000..9490974
--- /dev/null
@@ -0,0 +1,246 @@
+#ifndef _NDB_BENCH_SERIALIZER_H_
+#define _NDB_BENCH_SERIALIZER_H_
+
+#include <stdint.h>
+#include "../macros.h"
+#include "../varint.h"
+
+typedef uint8_t *(*generic_write_fn)(uint8_t *, const uint8_t *);
+typedef const uint8_t *(*generic_read_fn)(const uint8_t *, uint8_t *);
+typedef const uint8_t *(*generic_failsafe_read_fn)(const uint8_t *, size_t, uint8_t *);
+typedef size_t (*generic_nbytes_fn)(const uint8_t *);
+typedef size_t (*generic_skip_fn)(const uint8_t *, uint8_t *);
+typedef size_t (*generic_failsafe_skip_fn)(const uint8_t *, size_t, uint8_t *);
+
+// wraps a real serializer, exposing generic functions
+template <typename Serializer>
+struct generic_serializer {
+  typedef typename Serializer::obj_type obj_type;
+
+  static inline uint8_t *
+  write(uint8_t *buf, const uint8_t *obj)
+  {
+    return Serializer::write(buf, *reinterpret_cast<const obj_type *>(obj));
+  }
+
+  static inline const uint8_t *
+  read(const uint8_t *buf, uint8_t *obj)
+  {
+    return Serializer::read(buf, reinterpret_cast<obj_type *>(obj));
+  }
+
+  // returns nullptr on failure
+  static inline const uint8_t *
+  failsafe_read(const uint8_t *buf, size_t nbytes, uint8_t *obj)
+  {
+    return Serializer::failsafe_read(
+        buf, nbytes, reinterpret_cast<obj_type *>(obj));
+  }
+
+  static inline size_t
+  nbytes(const uint8_t *obj)
+  {
+    return Serializer::nbytes(reinterpret_cast<const obj_type *>(obj));
+  }
+
+  static inline size_t
+  skip(const uint8_t *stream, uint8_t *rawv)
+  {
+    return Serializer::skip(stream, rawv);
+  }
+
+  // returns 0 on failure
+  static inline size_t
+  failsafe_skip(const uint8_t *stream, size_t nbytes, uint8_t *rawv)
+  {
+    return Serializer::failsafe_skip(stream, nbytes, rawv);
+  }
+
+  static inline constexpr size_t
+  max_nbytes()
+  {
+    return Serializer::max_bytes();
+  }
+};
+
+template <typename T, bool Compress>
+struct serializer {
+  typedef T obj_type;
+
+  static inline uint8_t *
+  write(uint8_t *buf, const T &obj)
+  {
+    T *p = (T *) buf;
+    *p = obj;
+    return (uint8_t *) (p + 1);
+  }
+
+  static inline const uint8_t *
+  read(const uint8_t *buf, T *obj)
+  {
+    const T *p = (const T *) buf;
+    *obj = *p;
+    return (const uint8_t *) (p + 1);
+  }
+
+  static inline const uint8_t *
+  failsafe_read(const uint8_t *buf, size_t nbytes, T *obj)
+  {
+    if (unlikely(nbytes < sizeof(T)))
+      return nullptr;
+    return read(buf, obj);
+  }
+
+  static inline size_t
+  nbytes(const T *obj)
+  {
+    return sizeof(*obj);
+  }
+
+  static inline size_t
+  skip(const uint8_t *stream, uint8_t *rawv)
+  {
+    if (rawv)
+      NDB_MEMCPY(rawv, stream, sizeof(T));
+    return sizeof(T);
+  }
+
+  static inline size_t
+  failsafe_skip(const uint8_t *stream, size_t nbytes, uint8_t *rawv)
+  {
+    if (unlikely(nbytes < sizeof(T)))
+      return 0;
+    if (rawv)
+      NDB_MEMCPY(rawv, stream, sizeof(T));
+    return sizeof(T);
+  }
+
+  static inline constexpr size_t
+  max_nbytes()
+  {
+    return sizeof(T);
+  }
+};
+
+// serializer<T, True> specializations
+template <>
+struct serializer<uint32_t, true> {
+  typedef uint32_t obj_type;
+
+  static inline uint8_t *
+  write(uint8_t *buf, uint32_t obj)
+  {
+    return write_uvint32(buf, obj);
+  }
+
+  static inline const uint8_t *
+  read(const uint8_t *buf, uint32_t *obj)
+  {
+    return read_uvint32(buf, obj);
+  }
+
+  static inline const uint8_t *
+  failsafe_read(const uint8_t *buf, size_t nbytes, uint32_t *obj)
+  {
+    return failsafe_read_uvint32(buf, nbytes, obj);
+  }
+
+  static inline size_t
+  nbytes(const uint32_t *obj)
+  {
+    return size_uvint32(*obj);
+  }
+
+  static inline size_t
+  skip(const uint8_t *stream, uint8_t *rawv)
+  {
+    return skip_uvint32(stream, rawv);
+  }
+
+  static inline size_t
+  failsafe_skip(const uint8_t *stream, size_t nbytes, uint8_t *rawv)
+  {
+    return failsafe_skip_uvint32(stream, nbytes, rawv);
+  }
+
+  static inline constexpr size_t
+  max_nbytes()
+  {
+    return 5;
+  }
+};
+
+template <>
+struct serializer<int32_t, true> {
+  typedef int32_t obj_type;
+
+  static inline uint8_t *
+  write(uint8_t *buf, int32_t obj)
+  {
+    const uint32_t v = encode(obj);
+    return serializer<uint32_t, true>::write(buf, v);
+  }
+
+  static inline const uint8_t *
+  read(const uint8_t *buf, int32_t *obj)
+  {
+    uint32_t v;
+    buf = serializer<uint32_t, true>::read(buf, &v);
+    *obj = decode(v);
+    return buf;
+  }
+
+  static inline const uint8_t *
+  failsafe_read(const uint8_t *buf, size_t nbytes, int32_t *obj)
+  {
+    uint32_t v;
+    buf = serializer<uint32_t, true>::failsafe_read(buf, nbytes, &v);
+    if (unlikely(!buf))
+      return 0;
+    *obj = decode(v);
+    return buf;
+  }
+
+  static inline size_t
+  nbytes(const int32_t *obj)
+  {
+    const uint32_t v = encode(*obj);
+    return serializer<uint32_t, true>::nbytes(&v);
+  }
+
+  static inline size_t
+  skip(const uint8_t *stream, uint8_t *rawv)
+  {
+    return skip_uvint32(stream, rawv);
+  }
+
+  static inline size_t
+  failsafe_skip(const uint8_t *stream, size_t nbytes, uint8_t *rawv)
+  {
+    return failsafe_skip_uvint32(stream, nbytes, rawv);
+  }
+
+  static inline constexpr size_t
+  max_nbytes()
+  {
+    return 5;
+  }
+
+private:
+  // zig-zag encoding from:
+  // http://code.google.com/p/protobuf/source/browse/trunk/src/google/protobuf/wire_format_lite.h
+
+  static inline ALWAYS_INLINE constexpr uint32_t
+  encode(int32_t value)
+  {
+    return (value << 1) ^ (value >> 31);
+  }
+
+  static inline ALWAYS_INLINE constexpr int32_t
+  decode(uint32_t value)
+  {
+    return (value >> 1) ^ -static_cast<int32_t>(value & 1);
+  }
+};
+
+#endif /* _NDB_BENCH_SERIALIZER_H_ */
diff --git a/silo/run.sh b/silo/run.sh
new file mode 100755 (executable)
index 0000000..6ba7fa7
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/bash
+cd out-perf.check.masstree/
+cd benchmarks/
+./dbtest --verbose -t 5
diff --git a/silo/scopedperf.hh b/silo/scopedperf.hh
new file mode 100644 (file)
index 0000000..18a712e
--- /dev/null
@@ -0,0 +1,794 @@
+/*
+ * Canonical location:
+ *   git+ssh://amsterdam.csail.mit.edu/home/am1/prof/proftools.git
+ *   under spmc/lib/scopedperf.hh
+ *
+ * Modified by stephentu to disable for non C++11 builds
+ */
+
+#ifndef _SCOPED_PERF_H_
+#define _SCOPED_PERF_H_
+
+#ifdef USE_PERF_CTRS
+
+#if !defined(XV6)
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/time.h>
+#endif
+
+namespace scopedperf {
+
+#if defined(XV6)
+typedef u32 uint;
+typedef u64 uint64_t;
+#endif
+
+/*
+ * statically enable/disable most of the generated code for profiling.
+ */
+class default_enabler {
+ public:
+  bool enabled() const { return true; }
+};
+
+class always_enabled {
+ public:
+  bool enabled() const { return true; }
+};
+
+class always_disabled {
+ public:
+  bool enabled() const { return false; }
+};
+
+/*
+ * get CPU id function type
+ */
+typedef int(*getcpu_fn)(void);
+
+/*
+ * spinlock: mostly to avoid pthread mutex sleeping.
+ */
+#if !defined(XV6_KERNEL)
+class spinlock {
+ public:
+  spinlock() : x(0) {}
+
+  void acquire() {
+    while (!__sync_bool_compare_and_swap(&x, 0, 1))
+      ;
+  }
+
+  void release() {
+    x = 0;
+  }
+
+ private:
+  volatile uint x;
+};
+#endif
+
+#if defined(XV6_KERNEL)
+using ::spinlock;
+
+static inline int sched_getcpu() {
+  return mycpu()->id;
+}
+#endif
+
+class scoped_spinlock {
+ public:
+  scoped_spinlock(spinlock *larg) : l(larg) {
+    l->acquire();
+    held = true;
+  }
+
+  void release() {
+    if (held)
+      l->release();
+    held = false;
+  }
+
+  ~scoped_spinlock() { release(); }
+
+ private:
+  spinlock *const l;
+  bool held;
+};
+
+
+/*
+ * vector & pair: for portability.
+ */
+template<class A, class B>
+struct pair {
+  A first;
+  B second;
+};
+
+template<class A, class B>
+pair<A, B>
+make_pair(const A &a, const B &b)
+{
+  pair<A, B> p;
+  p.first = a;
+  p.second = b;
+  return p;
+}
+
+template<class T>
+struct vector {
+  T _buf[128];
+  uint _cnt;
+
+  vector() : _cnt(0) {}
+  void insert_front(T e) {
+    assert(_cnt < sizeof(_buf) / sizeof(T));
+    memmove(&_buf[1], &_buf[0], _cnt * sizeof(T));
+    _buf[0] = e;
+    _cnt++;
+  }
+  void push_back(T e) {
+    assert(_cnt < sizeof(_buf) / sizeof(T));
+    _buf[_cnt] = e;
+    _cnt++;
+  }
+};
+
+template<class T>
+struct viter {
+  const vector<T> *_v;
+  int _pos;
+
+  viter(const vector<T> *v, int pos) : _v(v), _pos(pos) {}
+  bool operator!=(const viter &other) const { return _pos != other._pos; }
+  void operator++() { _pos++; }
+  T operator*() { return _v->_buf[_pos]; }
+};
+
+template<class T>
+viter<T>
+begin(const vector<T> &v)
+{
+  return viter<T>(&v, 0);
+}
+
+template<class T>
+viter<T>
+end(const vector<T> &v)
+{
+  return viter<T>(&v, v._cnt);
+}
+
+
+/*
+ * fast log-base-2, for histograms.
+ */
+static const uint8_t log2table[256] = {
+#define R2(x)   x,      x
+#define R4(x)   R2(x),  R2(x)
+#define R8(x)   R4(x),  R4(x)
+#define R16(x)  R8(x),  R8(x)
+#define R32(x)  R16(x), R16(x)
+#define R64(x)  R32(x), R32(x)
+#define R128(x) R64(x), R64(x)
+  0, 1, R2(2), R4(3), R8(4), R16(5), R32(6), R64(7), R128(8)
+#undef R2
+#undef R4
+#undef R8
+#undef R16
+#undef R32
+#undef R64
+#undef R128
+};
+
+template<class T, int Nbits>
+inline uintptr_t
+log2r(T v)
+{
+  if (Nbits == 8) {
+    return log2table[v];
+  } else {
+    T hi = v >> (Nbits/2);
+    if (hi)
+      return Nbits/2 + log2r<T, Nbits/2>(hi);
+    else
+      return log2r<T, Nbits/2>(v);
+  }
+}
+
+template<class T>
+uintptr_t
+log2(T v)
+{
+  return log2r<T, sizeof(T)*8>(v);
+}
+
+
+/*
+ * ctrgroup: a group of performance counters.
+ */
+
+template<typename... Counters>
+class ctrgroup_chain;
+
+template<>
+class ctrgroup_chain<> {
+ public:
+  ctrgroup_chain() {}
+  static const uint cg_nctr = 0;
+  void cg_get_samples(uint64_t *v) const {}
+  void cg_get_delta(uint64_t *delta, uint64_t *prev) const {}
+  vector<const char*> get_names() const { return {}; }
+};
+
+template<typename One, typename... Others>
+class ctrgroup_chain<One, Others...> : ctrgroup_chain<Others...> {
+ public:
+  ctrgroup_chain(One *x, Others*... y)
+    : ctrgroup_chain<Others...>(y...), ctr(x)
+  {
+    x->setup();
+  }
+
+  static const uint cg_nctr = 1 + ctrgroup_chain<Others...>::cg_nctr;
+
+  void cg_get_samples(uint64_t *v) const {
+    v[0] = ctr->sample();
+    ctrgroup_chain<Others...>::cg_get_samples(v+1);
+  }
+
+  void cg_get_delta(uint64_t *delta, uint64_t *prev) const {
+    uint64_t x = ctr->sample();
+    *delta = (x - *prev) & ctr->mask;
+    *prev = x;
+    ctrgroup_chain<Others...>::cg_get_delta(delta+1, prev+1);
+  }
+
+  vector<const char*> get_names() const {
+    vector<const char*> v = ctrgroup_chain<Others...>::get_names();
+    v.insert_front(ctr->name);
+    return v;
+  }
+
+ private:
+  const One *const ctr;
+};
+
+template<typename... Counters>
+ctrgroup_chain<Counters...>
+ctrgroup(Counters*... args)
+{
+  return ctrgroup_chain<Counters...>(args...);
+}
+
+
+/*
+ * perfsum: aggregating counter deltas across multiple CPUs.
+ */
+class perfsum_base {
+ public:
+  enum display_opt { show, hide };
+
+  perfsum_base(const char *n, display_opt d) : name(n), disp(d) {
+    scoped_spinlock x(get_sums_lock());
+    get_sums()->push_back(this);
+  }
+
+  static void printall(int w0 = 17, int w = 13) {
+    scoped_spinlock x(get_sums_lock());
+    auto sums = get_sums();
+    for (perfsum_base *ps: *sums)
+      if (ps->disp == show)
+        ps->print(w0, w);
+  }
+
+  static void resetall() {
+    scoped_spinlock x(get_sums_lock());
+    for (perfsum_base *ps: *get_sums())
+      ps->reset();
+  }
+
+  virtual void print(int w0, int w) const = 0;
+  virtual void reset() = 0;
+
+ protected:
+  template<class Row, class Callback>
+  static void print_row(const char *rowname, const Row &r,
+                       int w0, int w, Callback f)
+  {
+    std::cout << std::left << std::setw(w0) << rowname;
+    for (const auto &elem: r)
+      std::cout << std::left << std::setw(w) << f(elem) << " ";
+    std::cout << std::endl;
+  }
+
+  const char *name;
+  const display_opt disp;
+
+ private:
+  static vector<perfsum_base*> *get_sums() {
+    static vector<perfsum_base*> v;
+    return &v;
+  }
+
+  static spinlock *get_sums_lock() {
+    static spinlock l;
+    return &l;
+  }
+};
+
+static inline void
+compiler_barrier()
+{
+  /* Avoid compile-time reordering across performance counter reads */
+  __asm __volatile("" ::: "memory");
+}
+
+template<typename Enabler, typename... Counters>
+class perfsum_tmpl : public perfsum_base, public Enabler {
+ public:
+  perfsum_tmpl(const ctrgroup_chain<Counters...> *c,
+               const char *n, perfsum_base::display_opt d)
+    : perfsum_base(n, d), cg(c)
+  {
+  }
+
+  static const uint ps_nctr = ctrgroup_chain<Counters...>::cg_nctr;
+
+ protected:
+  const struct ctrgroup_chain<Counters...> *const cg;
+  enum { maxcpu = 256 };
+
+  template<class Stats, class T>
+  static uint64_t addcpus(const Stats stat[], T f) {
+    uint64_t tot = 0;
+    for (uint i = 0; i < maxcpu; i++)
+      tot += f(&stat[i]);
+    return tot;
+  }
+};
+
+/*
+ * perfsum_ctr: aggregate counts of performance events.
+ */
+template<typename Enabler, typename... Counters>
+class perfsum_ctr : public perfsum_tmpl<Enabler, Counters...> {
+ public:
+  perfsum_ctr(const ctrgroup_chain<Counters...> *c,
+             const char *n, perfsum_base::display_opt d)
+    : perfsum_tmpl<Enabler, Counters...>(c, n, d), base(0)
+  {
+    reset();
+  }
+
+  perfsum_ctr(const char *n,
+             const perfsum_ctr<Enabler, Counters...> *basesum,
+              perfsum_base::display_opt d)
+    : perfsum_tmpl<Enabler, Counters...>(basesum->cg, n, d), base(basesum)
+  {
+    reset();
+  }
+
+  void get_samples(uint64_t *s) const {
+    compiler_barrier();
+    perfsum_tmpl<Enabler, Counters...>::cg->cg_get_samples(s);
+    compiler_barrier();
+  }
+
+  void record(uint cpuid, uint64_t *s) {
+    uint64_t delta[perfsum_tmpl<Enabler, Counters...>::ps_nctr];
+
+    compiler_barrier();
+    perfsum_tmpl<Enabler, Counters...>::cg->cg_get_delta(delta, s);
+    compiler_barrier();
+
+    for (uint i = 0; i < perfsum_tmpl<Enabler, Counters...>::ps_nctr; i++)
+      stat[cpuid].sum[i] += delta[i];
+    stat[cpuid].count++;
+  }
+
+  void print(int w0, int w) const /* override */ {
+    if (!Enabler::enabled())
+      return;
+
+    auto &cg = perfsum_tmpl<Enabler, Counters...>::cg;
+    vector<pair<uint64_t, uint64_t> > p;
+    for (uint i = 0; i < cg->cg_nctr; i++) {
+      uint64_t b =
+       base ? this->addcpus(base->stat, [&](const stats *s) { return s->sum[i]; })
+            : this->addcpus(stat,       [&](const stats *s) { return s->count; });
+      p.push_back(make_pair(b,
+       this->addcpus(stat, [i](const stats *s) { return s->sum[i]; })));
+    }
+
+    this->print_row(perfsum_base::name, cg->get_names(), w0, w, [](const char *name)
+             { return name; });
+    this->print_row("  avg",   p, w0, w, [](const pair<uint64_t, uint64_t> &e)
+#if !defined(XV6)
+             { return ((double) e.second) / (double) e.first; }
+#else
+             { return e.second / e.first; }
+#endif
+              );
+    this->print_row("  total", p, w0, w, [](const pair<uint64_t, uint64_t> &e)
+             { return e.second; });
+    this->print_row("  count", p, w0, w, [](const pair<uint64_t, uint64_t> &e)
+             { return e.first; });
+  }
+
+  void reset() /* override */ {
+    memset(stat, 0, sizeof(stat));
+  }
+
+ private:
+  struct stats {
+    uint64_t count;
+    uint64_t sum[perfsum_tmpl<Enabler, Counters...>::ps_nctr];
+  } __attribute__((aligned (64)));
+
+  struct stats stat[perfsum_tmpl<Enabler, Counters...>::maxcpu];
+  const struct perfsum_ctr<Enabler, Counters...> *const base;
+};
+
+template<typename Enabler, typename... Counters>
+class perfsum_ctr_inlinegroup :
+  public ctrgroup_chain<Counters...>,
+  public perfsum_ctr<Enabler, Counters...>
+{
+ public:
+  perfsum_ctr_inlinegroup(const char *n, perfsum_base::display_opt d,
+                          Counters*... ctrs)
+    : ctrgroup_chain<Counters...>(ctrs...),
+      perfsum_ctr<Enabler, Counters...>(this, n, d) {}
+};
+
+template<typename Enabler = default_enabler, typename... Counters>
+perfsum_ctr<Enabler, Counters...>
+perfsum(const char *name, const ctrgroup_chain<Counters...> *c,
+       const perfsum_base::display_opt d = perfsum_base::show)
+{
+  return perfsum_ctr<Enabler, Counters...>(c, name, d);
+}
+
+template<typename Enabler = default_enabler, typename... Counters>
+perfsum_ctr_inlinegroup<Enabler, Counters...>
+perfsum_group(const char *name, Counters*... c)
+{
+  return perfsum_ctr_inlinegroup<Enabler, Counters...>(name, perfsum_base::show, c...);
+}
+
+template<typename Enabler, typename... Counters>
+perfsum_ctr<Enabler, Counters...>
+perfsum_frac(const char *name,
+            const perfsum_ctr<Enabler, Counters...> *base)
+{
+  return perfsum_ctr<Enabler, Counters...>(name, base, perfsum_base::show);
+}
+
+/*
+ * perfsum_hist: histogram-based aggregates.
+ */
+template<typename Enabler, typename... Counters>
+class perfsum_hist_tmpl : public perfsum_tmpl<Enabler, Counters...> {
+ public:
+  perfsum_hist_tmpl(const ctrgroup_chain<Counters...> *c,
+                   const char *n, perfsum_base::display_opt d)
+    : perfsum_tmpl<Enabler, Counters...>(c, n, d)
+  {
+    reset();
+  }
+
+  void get_samples(uint64_t *s) const {
+    compiler_barrier();
+    perfsum_tmpl<Enabler, Counters...>::cg->cg_get_samples(s);
+    compiler_barrier();
+  }
+
+  void record(uint cpuid, uint64_t *s) {
+    uint64_t delta[perfsum_tmpl<Enabler, Counters...>::ps_nctr];
+
+    compiler_barrier();
+    perfsum_tmpl<Enabler, Counters...>::cg->cg_get_delta(delta, s);
+    compiler_barrier();
+
+    for (uint i = 0; i < perfsum_tmpl<Enabler, Counters...>::ps_nctr; i++)
+      stat[cpuid].hist[i].count[log2(delta[i])]++;
+  }
+
+  void print(int w0, int w) const /* override */ {
+    if (!Enabler::enabled())
+      return;
+
+    uint first = nbuckets, last = 0;
+
+    auto &cg = perfsum_tmpl<Enabler, Counters...>::cg;
+    vector<buckets> p;
+    for (uint i = 0; i < cg->cg_nctr; i++) {
+      buckets v;
+      for (uint j = 0; j < nbuckets; j++) {
+        v.count[j] = this->addcpus(stat, [&](const stats *s) { return s->hist[i].count[j]; });
+        if (v.count[j]) {
+          if (j < first) first = j;
+          if (j > last)  last = j;
+        }
+      }
+      p.push_back(v);
+    }
+
+    this->print_row(perfsum_base::name, cg->get_names(), w0, w, [](const char *name)
+             { return name; });
+    for (uint i = first; i <= last; i++) {
+      char n[64];
+      snprintf(n, sizeof(n), "  < 2^%d", i);
+      this->print_row(n, p, w0, w, [&](const buckets &b) { return b.count[i]; });
+    }
+    this->print_row("  total", p, w0, w, [](const buckets &b)
+                    { uint64_t s = 0; for (auto x: b.count) s += x; return s; });
+  }
+
+  void reset() /* override */ {
+    memset(stat, 0, sizeof(stat));
+  }
+
+ private:
+  enum { nbuckets = sizeof(uint64_t)*8 + 1 };
+
+  struct buckets {
+    uint64_t count[nbuckets];
+  };
+
+  struct stats {
+    struct buckets hist[perfsum_tmpl<Enabler, Counters...>::ps_nctr];
+  } __attribute__((aligned (64)));
+
+  struct stats stat[perfsum_tmpl<Enabler, Counters...>::maxcpu];
+};
+
+template<typename Enabler = default_enabler, typename... Counters>
+perfsum_hist_tmpl<Enabler, Counters...>
+perfsum_hist(const char *name, const ctrgroup_chain<Counters...> *c,
+            const perfsum_base::display_opt d = perfsum_base::show)
+{
+  return perfsum_hist_tmpl<Enabler, Counters...>(c, name, d);
+}
+
+
+/*
+ * namedctr &c: actual counter implementations.
+ */
+template<uint64_t CounterWidth>
+class namedctr {
+ public:
+  namedctr(const char *n) : name(n) {}
+  void setup() {}
+  const char *name;
+  static const uint64_t mask =
+    ((1ULL << (CounterWidth - 1)) - 1) << 1 | 1;
+};
+
+class tsc_ctr : public namedctr<64> {
+ public:
+  tsc_ctr() : namedctr("tsc") {}
+  static uint64_t sample() {
+    uint64_t a, d;
+    __asm __volatile("rdtsc" : "=a" (a), "=d" (d));
+    return a | (d << 32);
+  }
+};
+
+class tscp_ctr : public namedctr<64> {
+ public:
+  tscp_ctr() : namedctr("tscp") {}
+  static uint64_t sample() {
+    uint64_t a, d, c;
+    __asm __volatile("rdtscp" : "=a" (a), "=d" (d), "=c" (c));
+    return a | (d << 32);
+  }
+};
+
+template<uint64_t CounterWidth>
+class pmc_ctr : public namedctr<CounterWidth> {
+ public:
+  pmc_ctr(int n) : namedctr<CounterWidth>(mkname(n)), cn(n) {}
+  pmc_ctr(const char *nm) : namedctr<CounterWidth>(nm), cn(-1) {}
+
+  uint64_t sample() const {
+    uint64_t a, d;
+    __asm __volatile("rdpmc" : "=a" (a), "=d" (d) : "c" (cn));
+    return a | (d << 32);
+  }
+
+  int cn;
+
+ private:
+  static const char* mkname(int n) {
+    char *buf = new char[32];
+    snprintf(buf, 32, "pmc%d", n);
+    return buf;
+  }
+};
+
+template<uint64_t CounterWidth = 64>
+class pmc_setup : public pmc_ctr<CounterWidth> {
+ public:
+  pmc_setup(uint64_t v, const char *nm)
+    : pmc_ctr<CounterWidth>(nm), pmc_v(v) {}
+
+  void setup() {
+    if (pmc_ctr<CounterWidth>::cn >= 0)
+      return;
+
+    /*
+     * XXX detect how many counters the hardware has
+     */
+    static bool pmcuse[4];
+    static spinlock pmcuselock;
+
+    int n = 0;
+    scoped_spinlock x(&pmcuselock);
+    while (n < 4 && pmcuse[n])
+      n++;
+    assert(n < 4);
+    pmcuse[n] = true;
+    x.release();
+
+#if !defined(XV6)
+    // ugly but effective
+    std::stringstream ss;
+    ss << "for f in /sys/kernel/spmc/cpu*/" << n << "; do "
+       << "echo " << std::hex << pmc_v << " > $f; done";
+    assert(0 == system(ss.str().c_str()));
+#endif
+
+    pmc_ctr<CounterWidth>::cn = n;
+  }
+
+ private:
+  uint64_t pmc_v;
+};
+
+#if !defined(XV6)
+class tod_ctr : public namedctr<64> {
+ public:
+  tod_ctr() : namedctr("tod-usec") {}
+  uint64_t sample() const {
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return ((uint64_t) tv.tv_usec) + ((uint64_t) tv.tv_sec) * 1000000;
+  }
+};
+#endif
+
+class zero_ctr : public namedctr<64> {
+ public:
+  zero_ctr() : namedctr("zero") {}
+  uint64_t sample() const { return 0; }
+};
+
+
+/*
+ * scoped performance-counting regions, which record samples into a perfsum.
+ */
+template<typename Perfsum>
+class base_perf_region {
+ public:
+  base_perf_region(Perfsum *psarg, getcpu_fn getcpu)
+    : ps(psarg), enabled(ps->enabled()), cpuid(enabled ? getcpu() : 0)
+  {
+    if (enabled)
+      ps->get_samples(s);
+  }
+
+  // invoke lap multiple times to precisely measure iterations
+  // (use same measurement for end of one & start of next round)
+  void lap() {
+    if (enabled)
+      ps->record(cpuid, s);
+  }
+
+ private:
+  Perfsum *const ps;
+  const bool enabled;
+  const uint cpuid;
+  uint64_t s[Perfsum::ps_nctr];
+};
+
+template<typename Perfsum>
+class scoped_perf_region : public base_perf_region<Perfsum> {
+ public:
+  scoped_perf_region(Perfsum *psarg, getcpu_fn getcpu)
+    : base_perf_region<Perfsum>(psarg, getcpu) {}
+  ~scoped_perf_region() { base_perf_region<Perfsum>::lap(); }
+};
+
+template<typename Perfsum>
+class killable_perf_region : public base_perf_region<Perfsum> {
+ public:
+  killable_perf_region(Perfsum *psarg, getcpu_fn getcpu)
+    : base_perf_region<Perfsum>(psarg, getcpu), active(true) {}
+  ~killable_perf_region() { stop(); }
+
+  // perform a final measurement, if needed before destructor
+  void stop() {
+    if (active)
+      base_perf_region<Perfsum>::lap();
+    active = false;
+  }
+
+  // prevent destructor from performing a measurement
+  void kill() { active = false; }
+
+ private:
+  bool active;
+};
+
+template<typename Perfsum>
+scoped_perf_region<Perfsum>
+perf_region(Perfsum *ps, getcpu_fn getcpu = sched_getcpu)
+{
+  return scoped_perf_region<Perfsum>(ps, getcpu);
+}
+
+template<typename Perfsum>
+killable_perf_region<Perfsum>
+killable_region(Perfsum *ps, getcpu_fn getcpu = sched_getcpu)
+{
+  return killable_perf_region<Perfsum>(ps, getcpu);
+}
+
+
+/*
+ * macros for the common case of putting in a scoped perf-counting region.
+ */
+#define __PERF_CONCAT2(a, b)  a ## b
+#define __PERF_CONCAT(a, b)   __PERF_CONCAT2(a, b)
+#define __PERF_ANON          __PERF_CONCAT(__anon_id_, __COUNTER__)
+
+#define __PERF_REGION(region_var, sum_var, region_type, text, group)          \
+  static auto __PERF_CONCAT(sum_var, _sum) = scopedperf::perfsum(text, group); \
+  auto region_var = region_type(&__PERF_CONCAT(sum_var, _sum));
+
+#define ANON_REGION(text, group) \
+  __PERF_REGION(__PERF_ANON, __PERF_ANON, scopedperf::perf_region, text, group)
+#define PERF_REGION(var, text, group) \
+  __PERF_REGION(var, __PERF_ANON, scopedperf::perf_region, text, group)
+#define KILLABLE_REGION(var, text, group) \
+  __PERF_REGION(var, __PERF_ANON, scopedperf::killable_region, text, group)
+
+#define STATIC_COUNTER_DECL(ctrtype, ctrname, groupname) \
+  static ctrtype ctrname; \
+  static ::scopedperf::ctrgroup_chain< ctrtype > groupname(&ctrname);
+#define PERF_EXPR(expr) expr
+#define PERF_DECL(decl) decl
+
+#define CLASS_STATIC_COUNTER_DECL(ctrtype, ctrname, groupname) \
+  static ctrtype ctrname; \
+  static ::scopedperf::ctrgroup_chain< ctrtype > groupname;
+#define CLASS_STATIC_COUNTER_IMPL(clsname, ctrtype, ctrname, groupname) \
+  ctrtype clsname::ctrname; \
+  ::scopedperf::ctrgroup_chain< ctrtype > clsname::groupname(&ctrname) ;
+
+} /* namespace scopedperf */
+
+#else /* !USE_PERF_CTRS */
+
+#define ANON_REGION(text, group) ((void)0)
+#define PERF_REGION(var, text, group) ((void)0)
+#define KILLABLE_REGION(var, text, group) ((void)0)
+
+#define STATIC_COUNTER_DECL(ctrtype, ctrname, groupname)
+#define PERF_EXPR(expr) ((void)0)
+#define PERF_DECL(decl)
+
+#define CLASS_STATIC_COUNTER_DECL(ctrtype, ctrname, groupname)
+#define CLASS_STATIC_COUNTER_IMPL(clsname, ctrtype, ctrname, groupname)
+
+#endif /* USE_PERF_CTRS */
+
+#endif /* _SCOPED_PERF_H_ */
diff --git a/silo/scripts/tester.py b/silo/scripts/tester.py
new file mode 100755 (executable)
index 0000000..f632fa0
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+
+import itertools
+import platform
+import subprocess
+import sys
+import math
+import pickle
+
+def normalize(x):
+  denom = sum(x)
+  return [e/denom for e in x]
+
+def scale(x, a):
+  return [e * a for e in x]
+
+def argcmp(x, comp, predicate):
+  idx = None
+  val = None
+  for i in xrange(len(x)):
+    if not predicate(x[i]):
+      continue
+    if idx is None or comp(x[i], val):
+      idx = i
+      val = x[i]
+  if idx is None:
+    # couldn't find it
+    raise Exception("no argmin satisfiying predicate")
+  return idx
+
+def argmin(x, predicate):
+  return argcmp(x, lambda a, b: a < b, predicate)
+
+def argmax(x, predicate):
+  return argcmp(x, lambda a, b: a > b, predicate)
+
+def allocate(nworkers, weights):
+  approx = map(int, map(math.ceil, scale(weights, nworkers)))
+  diff = sum(approx) - nworkers
+  if diff > 0:
+    while diff > 0:
+      i = argmin(approx, predicate=lambda x: x > 0)
+      approx[i] -= 1
+      diff -= 1
+  elif diff < 0:
+    i = argmax(approx, lambda x: True)
+    approx[i] += -diff
+  acc = 0
+  ret = []
+  for x in approx:
+    ret.append(range(acc, acc + x))
+    acc += x
+  return ret
+
+def run(cmd):
+  print >>sys.stderr, '[INFO] running command %s' % str(cmd)
+  p = subprocess.Popen(cmd, stdin=open('/dev/null', 'r'), stdout=subprocess.PIPE)
+  r = p.stdout.read()
+  p.wait()
+  return r
+
+if __name__ == '__main__':
+  (_, outfile) = sys.argv
+
+  STRATEGIES = ['epoch', 'epoch-compress']
+  NCORES = [1, 2, 4, 8, 16, 24, 32]
+  WSET = [18]
+
+  #STRATEGIES = ['epoch']
+  #NCORES = [1]
+  #WSET = [18]
+
+  node = platform.node()
+
+  if node == 'modis2':
+    LOGGERS = [
+        ('data.log', 1.),
+        ('/data/scidb/001/2/stephentu/data.log', 1.),
+        ('/data/scidb/001/3/stephentu/data.log', 1.),
+    ]
+  elif node == 'istc3':
+    LOGGERS = [
+        ('data.log', 1./3.),
+        ('/f0/stephentu/data.log', 2./3.),
+    ]
+  else:
+    print "unknown node", node
+    assert False, "Unknown node!"
+
+  weights = normalize([x[1] for x in LOGGERS])
+  logfile_cmds = list(itertools.chain.from_iterable([['--logfile', f] for f, _ in LOGGERS]))
+
+  results = []
+  for strat, ncores, ws in itertools.product(STRATEGIES, NCORES, WSET):
+    allocations = allocate(ncores, weights)
+    alloc_cmds = list(
+        itertools.chain.from_iterable([['--assignment', ','.join(map(str, alloc))] for alloc in allocations]))
+    cmd = ['./persist_test'] + \
+        logfile_cmds + \
+        alloc_cmds + \
+        ['--num-threads', str(ncores),
+         '--strategy', strat,
+         '--writeset', str(ws),
+         '--valuesize', '32']
+    output = run(cmd)
+    res = float(output.strip())
+    results.append(((strat, ncores, ws), res))
+
+  with open(outfile, 'w') as fp:
+    pickle.dump(results, fp)
diff --git a/silo/scripts/tester.sh b/silo/scripts/tester.sh
new file mode 100755 (executable)
index 0000000..ba5cbb2
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+for ncores in 1 2 4 8 16 24 32; do
+  #for wset in 4 8 16; do
+  for wset in 18; do
+    echo -n "$ncores $wset "
+    ./persist_test \
+      --logfile data.log \
+      --logfile /data/scidb/001/2/stephentu/data.log \
+      --logfile /data/scidb/001/3/stephentu/data.log \
+      --num-threads $ncores --strategy epoch --writeset $wset --valuesize 32
+    sleep 1
+  done
+done
diff --git a/silo/small_unordered_map.h b/silo/small_unordered_map.h
new file mode 100644 (file)
index 0000000..ad549e9
--- /dev/null
@@ -0,0 +1,511 @@
+#ifndef _SMALL_UNORDERED_MAP_H_
+#define _SMALL_UNORDERED_MAP_H_
+
+#include <algorithm>
+#include <iterator>
+#include <stdint.h>
+#include <unordered_map>
+#include <type_traits>
+
+#include "macros.h"
+#include "counter.h"
+#include "log2.hh"
+#include "ndb_type_traits.h"
+
+namespace private_ {
+
+  template <typename T>
+  struct is_eq_expensive { static const bool value = true; };
+
+  struct cheap_eq { static const bool value = false; };
+
+  // equals is cheap for integer types
+  template <> struct is_eq_expensive<bool>     : public cheap_eq {};
+  template <> struct is_eq_expensive<uint8_t>  : public cheap_eq {};
+  template <> struct is_eq_expensive<int8_t>   : public cheap_eq {};
+  template <> struct is_eq_expensive<uint16_t> : public cheap_eq {};
+  template <> struct is_eq_expensive<int16_t>  : public cheap_eq {};
+  template <> struct is_eq_expensive<uint32_t> : public cheap_eq {};
+  template <> struct is_eq_expensive<int32_t>  : public cheap_eq {};
+  template <> struct is_eq_expensive<uint64_t> : public cheap_eq {};
+  template <> struct is_eq_expensive<int64_t>  : public cheap_eq {};
+
+  static event_avg_counter evt_avg_max_unordered_map_chain_length CACHE_ALIGNED
+    ("avg_max_unordered_map_chain_length");
+
+  template <typename T>
+  struct fast_func_param {
+    typedef typename std::conditional<std::is_scalar<T>::value, T, const T &>::type type;
+  };
+
+  template <typename T>
+  struct myhash {
+    inline ALWAYS_INLINE size_t
+    operator()(typename fast_func_param<T>::type t) const
+    {
+      return std::hash<T>()(t);
+    }
+  };
+
+  template <typename Tp>
+  struct myhash<Tp *> {
+    inline ALWAYS_INLINE size_t
+    operator()(Tp *t) const
+    {
+      // std::hash for ptrs is bad
+      // tommyhash: http://tommyds.sourceforge.net/doc/tommyhash_8h_source.html
+      size_t key = reinterpret_cast<size_t>(t) >> 3; // shift out 8-byte pointer alignment
+#ifdef USE_TOMMY_HASH
+      key = (~key) + (key << 21); // key = (key << 21) - key - 1;
+      key = key ^ (key >> 24);
+      key = (key + (key << 3)) + (key << 8); // key * 265
+      key = key ^ (key >> 14);
+      key = (key + (key << 2)) + (key << 4); // key * 21
+      key = key ^ (key >> 28);
+      key = key + (key << 31);
+#else
+      key = (~key) + (key << 21); // key = (key << 21) - key - 1;
+      key = key ^ (key >> 24);
+#endif
+      return key;
+    }
+  };
+
+  static inline constexpr size_t
+  TableSize(size_t small_size)
+  {
+    return round_up_to_pow2_const(small_size);
+  }
+}
+
+/**
+ * For under SmallSize, uses linear probing on a fixed size array. Otherwise,
+ * delegates to a regular std::unordered_map
+ *
+ * XXX(stephentu): allow custom allocator
+ */
+template <typename Key,
+          typename T,
+          size_t SmallSize = SMALL_SIZE_MAP,
+          typename Hash = private_::myhash<Key>>
+class small_unordered_map {
+public:
+  typedef Key key_type;
+  typedef T mapped_type;
+  typedef std::pair<const key_type, mapped_type> value_type;
+  typedef Hash hasher;
+  typedef T & reference;
+  typedef const T & const_reference;
+
+private:
+  typedef std::unordered_map<Key, T, Hash> large_table_type;
+  typedef std::pair<key_type, mapped_type> bucket_value_type;
+
+  typedef typename large_table_type::iterator large_table_iterator;
+  typedef typename large_table_type::const_iterator large_table_const_iterator;
+
+  static const bool is_trivially_destructible =
+    private_::is_trivially_destructible<bucket_value_type>::value;
+
+  static const size_t TableSize = private_::TableSize(SmallSize);
+  static_assert(SmallSize >= 1, "XXX");
+  static_assert(TableSize >= 1, "XXX");
+
+  struct bucket {
+    inline ALWAYS_INLINE bucket_value_type *
+    ptr()
+    {
+      return reinterpret_cast<bucket_value_type *>(&buf[0]);
+    }
+
+    inline ALWAYS_INLINE const bucket_value_type *
+    ptr() const
+    {
+      return reinterpret_cast<const bucket_value_type *>(&buf[0]);
+    }
+
+    inline ALWAYS_INLINE bucket_value_type &
+    ref()
+    {
+      return *ptr();
+    }
+
+    inline ALWAYS_INLINE const bucket_value_type &
+    ref() const
+    {
+      return *ptr();
+    }
+
+    template <class... Args>
+    inline ALWAYS_INLINE void
+    construct(size_t hash, Args &&... args)
+    {
+      h = hash;
+      new (&ref()) bucket_value_type(std::forward<Args>(args)...);
+    }
+
+    inline ALWAYS_INLINE void
+    destroy()
+    {
+      if (!is_trivially_destructible)
+        ref().~bucket_value_type();
+    }
+
+    struct bucket *bnext;
+    size_t h;
+    char buf[sizeof(value_type)];
+  };
+
+  // iterators are not stable across mutation
+  template <typename SmallIterType,
+            typename LargeIterType,
+            typename ValueType>
+  class iterator_ : public std::iterator<std::forward_iterator_tag, ValueType> {
+    friend class small_unordered_map;
+  public:
+    inline iterator_() : large(false), b(0) {}
+
+    template <typename S, typename L, typename V>
+    inline iterator_(const iterator_<S, L, V> &other)
+      : large(other.large), b(other.b)
+    {}
+
+    inline ValueType &
+    operator*() const
+    {
+      if (unlikely(large))
+        return *large_it;
+      return reinterpret_cast<ValueType &>(b->ref());
+    }
+
+    inline ValueType *
+    operator->() const
+    {
+      if (unlikely(large))
+        return &(*large_it);
+      return reinterpret_cast<ValueType *>(b->ptr());
+    }
+
+    inline bool
+    operator==(const iterator_ &o) const
+    {
+      INVARIANT(large == o.large);
+      if (likely(!large))
+        return b == o.b;
+      return large_it == o.large_it;
+    }
+
+    inline bool
+    operator!=(const iterator_ &o) const
+    {
+      return !operator==(o);
+    }
+
+    inline iterator_ &
+    operator++()
+    {
+      if (unlikely(large)) {
+        ++large_it;
+        return *this;
+      }
+      b++;
+      return *this;
+    }
+
+    inline iterator_
+    operator++(int)
+    {
+      iterator_ cur = *this;
+      ++(*this);
+      return cur;
+    }
+
+  protected:
+    inline iterator_(SmallIterType *b)
+      : large(false), b(b)
+    {
+    }
+    inline iterator_(LargeIterType large_it)
+      : large(true), large_it(large_it) {}
+
+  private:
+    bool large;
+    SmallIterType *b;
+    LargeIterType large_it;
+  };
+
+  static size_t
+  chain_length(bucket *b)
+  {
+    size_t ret = 0;
+    while (b) {
+      ret++;
+      b = b->bnext;
+    }
+    return ret;
+  }
+
+public:
+
+  typedef
+    iterator_<
+      bucket,
+      large_table_iterator,
+      value_type>
+    iterator;
+
+  typedef
+    iterator_<
+      const bucket,
+      large_table_const_iterator,
+      const value_type>
+    const_iterator;
+
+  small_unordered_map()
+    : n(0), large_elems(0)
+  {
+    NDB_MEMSET(&table[0], 0, sizeof(table));
+  }
+
+  ~small_unordered_map()
+  {
+    if (unlikely(large_elems)) {
+      delete large_elems;
+      return;
+    }
+    for (size_t i = 0; i < n; i++)
+      small_elems[i].destroy();
+  }
+
+  small_unordered_map(const small_unordered_map &other)
+    : n(0), large_elems(0)
+  {
+    NDB_MEMSET(&table[0], 0, sizeof(table));
+    assignFrom(other);
+  }
+
+  small_unordered_map &
+  operator=(const small_unordered_map &other)
+  {
+    // self assignment
+    if (unlikely(this == &other))
+      return *this;
+    assignFrom(other);
+    return *this;
+  }
+
+private:
+  bucket *
+  find_bucket(const key_type &k, size_t *hash_value)
+  {
+    INVARIANT(!large_elems);
+    const size_t h = Hash()(k);
+    if (hash_value)
+      *hash_value = h;
+    const size_t i = h % TableSize;
+    bucket *b = table[i];
+    while (b) {
+      const bool check_hash = private_::is_eq_expensive<key_type>::value;
+      if ((!check_hash || b->h == h) && b->ref().first == k)
+        return b;
+      b = b->bnext;
+    }
+    return 0;
+  }
+
+  inline ALWAYS_INLINE const bucket *
+  find_bucket(const key_type &k, size_t *hash_value) const
+  {
+    return const_cast<small_unordered_map *>(this)->find_bucket(k, hash_value);
+  }
+
+public:
+
+  // XXX(stephentu): template away this stuff
+
+  mapped_type &
+  operator[](const key_type &k)
+  {
+    if (unlikely(large_elems))
+      return large_elems->operator[](k);
+    size_t h;
+    bucket *b = find_bucket(k, &h);
+    if (b)
+      return b->ref().second;
+    if (unlikely(n == SmallSize)) {
+      large_elems = new large_table_type;
+      for (size_t n = 0; n < SmallSize; n++) {
+        bucket &b = small_elems[n];
+#if GCC_AT_LEAST_47
+        large_elems->emplace(std::move(b.ref()));
+#else
+        large_elems->operator[](std::move(b.ref().first)) = std::move(b.ref().second);
+#endif
+        b.destroy();
+      }
+      n = 0;
+      return large_elems->operator[](k);
+    }
+    INVARIANT(n < SmallSize);
+    b = &small_elems[n++];
+    b->construct(h, k, mapped_type());
+    const size_t i = h % TableSize;
+    b->bnext = table[i];
+    table[i] = b;
+    return b->ref().second;
+  }
+
+  mapped_type &
+  operator[](key_type &&k)
+  {
+    if (unlikely(large_elems))
+      return large_elems->operator[](std::move(k));
+    size_t h;
+    bucket *b = find_bucket(k, &h);
+    if (b)
+      return b->ref().second;
+    if (unlikely(n == SmallSize)) {
+      large_elems = new large_table_type;
+      for (size_t n = 0; n < SmallSize; n++) {
+        bucket &b = small_elems[n];
+#if GCC_AT_LEAST_47
+        large_elems->emplace(std::move(b.ref()));
+#else
+        large_elems->operator[](std::move(b.ref().first)) = std::move(b.ref().second);
+#endif
+        b.destroy();
+      }
+      n = 0;
+      return large_elems->operator[](std::move(k));
+    }
+    INVARIANT(n < SmallSize);
+    b = &small_elems[n++];
+    b->construct(h, std::move(k), mapped_type());
+    const size_t i = h % TableSize;
+    b->bnext = table[i];
+    table[i] = b;
+    return b->ref().second;
+  }
+
+  inline size_t
+  size() const
+  {
+    if (unlikely(large_elems))
+      return large_elems->size();
+    return n;
+  }
+
+  inline bool
+  empty() const
+  {
+    return size() == 0;
+  }
+
+  iterator
+  begin()
+  {
+    if (unlikely(large_elems))
+      return iterator(large_elems->begin());
+    return iterator(&small_elems[0]);
+  }
+
+  const_iterator
+  begin() const
+  {
+    if (unlikely(large_elems))
+      return const_iterator(large_elems->begin());
+    return const_iterator(&small_elems[0]);
+  }
+
+  inline iterator
+  end()
+  {
+    if (unlikely(large_elems))
+      return iterator(large_elems->end());
+    return iterator(&small_elems[n]);
+  }
+
+  inline const_iterator
+  end() const
+  {
+    if (unlikely(large_elems))
+      return const_iterator(large_elems->end());
+    return const_iterator(&small_elems[n]);
+  }
+
+  iterator
+  find(const key_type &k)
+  {
+    if (unlikely(large_elems))
+      return iterator(large_elems->find(k));
+    bucket * const b = find_bucket(k, 0);
+    if (b)
+      return iterator(b);
+    return end();
+  }
+
+  const_iterator
+  find(const key_type &k) const
+  {
+    if (unlikely(large_elems))
+      return const_iterator(large_elems->find(k));
+    const bucket * const b = find_bucket(k, 0);
+    if (b)
+      return const_iterator(b);
+    return end();
+  }
+
+  void
+  clear()
+  {
+    if (unlikely(large_elems)) {
+      INVARIANT(!n);
+      delete large_elems;
+      large_elems = NULL;
+      return;
+    }
+    if (!n)
+      return;
+    NDB_MEMSET(&table[0], 0, sizeof(table));
+    for (size_t i = 0; i < n; i++)
+      small_elems[i].destroy();
+    n = 0;
+  }
+
+public:
+  // non-standard API
+  inline bool is_small_type() const { return !large_elems; }
+
+private:
+
+  // doesn't check for self assignment
+  inline void
+  assignFrom(const small_unordered_map &that)
+  {
+    clear();
+    if (that.large_elems) {
+      INVARIANT(!that.n);
+      INVARIANT(!n);
+      large_elems = new large_table_type(*that.large_elems);
+      return;
+    }
+    INVARIANT(!large_elems);
+    for (size_t i = 0; i < that.n; i++) {
+      bucket * const b = &small_elems[n++];
+      const bucket * const that_b = &that.small_elems[i];
+      b->construct(that_b->h, that_b->ref().first, that_b->ref().second);
+      const size_t idx = b->h % TableSize;
+      b->bnext = table[idx];
+      table[idx] = b;
+    }
+  }
+
+  size_t n;
+
+  bucket small_elems[SmallSize];
+  bucket *table[TableSize];
+
+  large_table_type *large_elems;
+};
+
+#endif /* _SMALL_UNORDERED_MAP_H_ */
diff --git a/silo/small_vector.h b/silo/small_vector.h
new file mode 100644 (file)
index 0000000..f09f3b2
--- /dev/null
@@ -0,0 +1,648 @@
+#ifndef _SMALL_VECTOR_H_
+#define _SMALL_VECTOR_H_
+
+#include <algorithm>
+#include <vector>
+#include <type_traits>
+
+#include "macros.h"
+#include "ndb_type_traits.h"
+
+/**
+ * References are not guaranteed to be stable across mutation
+ *
+ * XXX(stephentu): allow custom allocator
+ */
+template <typename T, size_t SmallSize = SMALL_SIZE_VEC>
+class small_vector {
+  typedef std::vector<T> large_vector_type;
+
+  static const bool is_trivially_destructible =
+    private_::is_trivially_destructible<T>::value;
+
+  // std::is_trivially_copyable not supported in g++-4.7
+  static const bool is_trivially_copyable = std::is_scalar<T>::value;
+
+public:
+
+  typedef T value_type;
+  typedef T & reference;
+  typedef const T & const_reference;
+  typedef size_t size_type;
+
+  small_vector() : n(0), large_elems(0) {}
+  ~small_vector()
+  {
+    clearDestructive();
+  }
+
+  small_vector(const small_vector &that)
+    : n(0), large_elems(0)
+  {
+    assignFrom(that);
+  }
+
+  // not efficient, don't use in performance critical parts
+  small_vector(std::initializer_list<T> l)
+    : n(0), large_elems(nullptr)
+  {
+    if (l.size() > SmallSize) {
+      large_elems = new large_vector_type(l);
+    } else {
+      for (auto &p : l)
+        push_back(p);
+    }
+  }
+
+  small_vector &
+  operator=(const small_vector &that)
+  {
+    assignFrom(that);
+    return *this;
+  }
+
+  inline size_t
+  size() const
+  {
+    if (unlikely(large_elems))
+      return large_elems->size();
+    return n;
+  }
+
+  inline bool
+  empty() const
+  {
+    return size() == 0;
+  }
+
+  inline reference
+  front()
+  {
+    if (unlikely(large_elems))
+      return large_elems->front();
+    INVARIANT(n > 0);
+    INVARIANT(n <= SmallSize);
+    return *ptr();
+  }
+
+  inline const_reference
+  front() const
+  {
+    return const_cast<small_vector *>(this)->front();
+  }
+
+  inline reference
+  back()
+  {
+    if (unlikely(large_elems))
+      return large_elems->back();
+    INVARIANT(n > 0);
+    INVARIANT(n <= SmallSize);
+    return ptr()[n - 1];
+  }
+
+  inline const_reference
+  back() const
+  {
+    return const_cast<small_vector *>(this)->back();
+  }
+
+  inline void
+  pop_back()
+  {
+    if (unlikely(large_elems)) {
+      large_elems->pop_back();
+      return;
+    }
+    INVARIANT(n > 0);
+    if (!is_trivially_destructible)
+      ptr()[n - 1].~T();
+    n--;
+  }
+
+  inline void
+  push_back(const T &obj)
+  {
+    emplace_back(obj);
+  }
+
+  inline void
+  push_back(T &&obj)
+  {
+    emplace_back(std::move(obj));
+  }
+
+  // C++11 goodness- a strange syntax this is
+
+  template <class... Args>
+  inline void
+  emplace_back(Args &&... args)
+  {
+    if (unlikely(large_elems)) {
+      INVARIANT(!n);
+      large_elems->emplace_back(std::forward<Args>(args)...);
+      return;
+    }
+    if (unlikely(n == SmallSize)) {
+      large_elems = new large_vector_type(ptr(), ptr() + n);
+      large_elems->emplace_back(std::forward<Args>(args)...);
+      n = 0;
+      return;
+    }
+    INVARIANT(n < SmallSize);
+    new (&(ptr()[n++])) T(std::forward<Args>(args)...);
+  }
+
+  inline reference
+  operator[](int i)
+  {
+    if (unlikely(large_elems))
+      return large_elems->operator[](i);
+    return ptr()[i];
+  }
+
+  inline const_reference
+  operator[](int i) const
+  {
+    return const_cast<small_vector *>(this)->operator[](i);
+  }
+
+  void
+  clear()
+  {
+    if (unlikely(large_elems)) {
+      INVARIANT(!n);
+      large_elems->clear();
+      return;
+    }
+    if (!is_trivially_destructible)
+      for (size_t i = 0; i < n; i++)
+        ptr()[i].~T();
+    n = 0;
+  }
+
+  inline void
+  reserve(size_t n)
+  {
+    if (unlikely(large_elems))
+      large_elems->reserve(n);
+  }
+
+  // non-standard API
+  inline bool is_small_type() const { return !large_elems; }
+
+  template <typename Compare = std::less<T>>
+  inline void
+  sort(Compare c = Compare())
+  {
+    if (unlikely(large_elems))
+      std::sort(large_elems->begin(), large_elems->end(), c);
+    else
+      std::sort(small_begin(), small_end(), c);
+  }
+
+private:
+
+  void
+  clearDestructive()
+  {
+    if (unlikely(large_elems)) {
+      INVARIANT(!n);
+      delete large_elems;
+      large_elems = NULL;
+      return;
+    }
+    if (!is_trivially_destructible)
+      for (size_t i = 0; i < n; i++)
+        ptr()[i].~T();
+    n = 0;
+  }
+
+  template <typename ObjType>
+  class small_iterator_ : public std::iterator<std::bidirectional_iterator_tag, ObjType> {
+    friend class small_vector;
+  public:
+    inline small_iterator_() : p(0) {}
+
+    template <typename O>
+    inline small_iterator_(const small_iterator_<O> &other)
+      : p(other.p)
+    {}
+
+    inline ObjType &
+    operator*() const
+    {
+      return *p;
+    }
+
+    inline ObjType *
+    operator->() const
+    {
+      return p;
+    }
+
+    inline bool
+    operator==(const small_iterator_ &o) const
+    {
+      return p == o.p;
+    }
+
+    inline bool
+    operator!=(const small_iterator_ &o) const
+    {
+      return !operator==(o);
+    }
+
+    inline bool
+    operator<(const small_iterator_ &o) const
+    {
+      return p < o.p;
+    }
+
+    inline bool
+    operator>=(const small_iterator_ &o) const
+    {
+      return !operator<(o);
+    }
+
+    inline bool
+    operator>(const small_iterator_ &o) const
+    {
+      return p > o.p;
+    }
+
+    inline bool
+    operator<=(const small_iterator_ &o) const
+    {
+      return !operator>(o);
+    }
+
+    inline small_iterator_ &
+    operator+=(int n)
+    {
+      p += n;
+      return *this;
+    }
+
+    inline small_iterator_ &
+    operator-=(int n)
+    {
+      p -= n;
+      return *this;
+    }
+
+    inline small_iterator_
+    operator+(int n) const
+    {
+      small_iterator_ cpy = *this;
+      return cpy += n;
+    }
+
+    inline small_iterator_
+    operator-(int n) const
+    {
+      small_iterator_ cpy = *this;
+      return cpy -= n;
+    }
+
+    inline intptr_t
+    operator-(const small_iterator_ &o) const
+    {
+      return p - o.p;
+    }
+
+    inline small_iterator_ &
+    operator++()
+    {
+      ++p;
+      return *this;
+    }
+
+    inline small_iterator_
+    operator++(int)
+    {
+      small_iterator_ cur = *this;
+      ++(*this);
+      return cur;
+    }
+
+    inline small_iterator_ &
+    operator--()
+    {
+      --p;
+      return *this;
+    }
+
+    inline small_iterator_
+    operator--(int)
+    {
+      small_iterator_ cur = *this;
+      --(*this);
+      return cur;
+    }
+
+  protected:
+    inline small_iterator_(ObjType *p) : p(p) {}
+
+  private:
+    ObjType *p;
+  };
+
+  template <typename ObjType, typename SmallTypeIter, typename LargeTypeIter>
+  class iterator_ : public std::iterator<std::bidirectional_iterator_tag, ObjType> {
+    friend class small_vector;
+  public:
+    inline iterator_() : large(false) {}
+
+    template <typename O, typename S, typename L>
+    inline iterator_(const iterator_<O, S, L> &other)
+      : large(other.large),
+        small_it(other.small_it),
+        large_it(other.large_it)
+    {}
+
+    inline ObjType &
+    operator*() const
+    {
+      if (unlikely(large))
+        return *large_it;
+      return *small_it;
+    }
+
+    inline ObjType *
+    operator->() const
+    {
+      if (unlikely(large))
+        return &(*large_it);
+      return &(*small_it);
+    }
+
+    inline bool
+    operator==(const iterator_ &o) const
+    {
+      if (unlikely(large))
+        return large_it == o.large_it;
+      return small_it == o.small_it;
+    }
+
+    inline bool
+    operator!=(const iterator_ &o) const
+    {
+      return !operator==(o);
+    }
+
+    inline bool
+    operator<(const iterator_ &o) const
+    {
+      if (unlikely(large))
+        return large_it < o.large_it;
+      return small_it < o.small_it;
+    }
+
+    inline bool
+    operator>=(const iterator_ &o) const
+    {
+      return !operator<(o);
+    }
+
+    inline bool
+    operator>(const iterator_ &o) const
+    {
+      if (unlikely(large))
+        return large_it > o.large_it;
+      return small_it > o.small_it;
+    }
+
+    inline bool
+    operator<=(const iterator_ &o) const
+    {
+      return !operator>(o);
+    }
+
+    inline iterator_ &
+    operator+=(int n)
+    {
+      if (unlikely(large))
+        large_it += n;
+      else
+        small_it += n;
+      return *this;
+    }
+
+    inline iterator_ &
+    operator-=(int n)
+    {
+      if (unlikely(large))
+        large_it -= n;
+      else
+        small_it -= n;
+      return *this;
+    }
+
+    inline iterator_
+    operator+(int n) const
+    {
+      iterator_ cpy = *this;
+      return cpy += n;
+    }
+
+    inline iterator_
+    operator-(int n) const
+    {
+      iterator_ cpy = *this;
+      return cpy -= n;
+    }
+
+    inline intptr_t
+    operator-(const iterator_ &o) const
+    {
+      if (unlikely(large))
+        return large_it - o.large_it;
+      else
+        return small_it - o.small_it;
+    }
+
+    inline iterator_ &
+    operator++()
+    {
+      if (unlikely(large))
+        ++large_it;
+      else
+        ++small_it;
+      return *this;
+    }
+
+    inline iterator_
+    operator++(int)
+    {
+      iterator_ cur = *this;
+      ++(*this);
+      return cur;
+    }
+
+    inline iterator_ &
+    operator--()
+    {
+      if (unlikely(large))
+        --large_it;
+      else
+        --small_it;
+      return *this;
+    }
+
+    inline iterator_
+    operator--(int)
+    {
+      iterator_ cur = *this;
+      --(*this);
+      return cur;
+    }
+
+  protected:
+    iterator_(SmallTypeIter small_it)
+      : large(false), small_it(small_it), large_it() {}
+    iterator_(LargeTypeIter large_it)
+      : large(true), small_it(), large_it(large_it) {}
+
+  private:
+    bool large;
+    SmallTypeIter small_it;
+    LargeTypeIter large_it;
+  };
+
+  typedef small_iterator_<T> small_iterator;
+  typedef small_iterator_<const T> const_small_iterator;
+  typedef typename large_vector_type::iterator large_iterator;
+  typedef typename large_vector_type::const_iterator const_large_iterator;
+
+  inline small_iterator
+  small_begin()
+  {
+    INVARIANT(!large_elems);
+    return small_iterator(ptr());
+  }
+
+  inline const_small_iterator
+  small_begin() const
+  {
+    INVARIANT(!large_elems);
+    return const_small_iterator(ptr());
+  }
+
+  inline small_iterator
+  small_end()
+  {
+    INVARIANT(!large_elems);
+    return small_iterator(ptr() + n);
+  }
+
+  inline const_small_iterator
+  small_end() const
+  {
+    INVARIANT(!large_elems);
+    return const_small_iterator(ptr() + n);
+  }
+
+public:
+
+  typedef iterator_<T, small_iterator, large_iterator> iterator;
+  typedef iterator_<const T, const_small_iterator, const_large_iterator> const_iterator;
+
+  typedef std::reverse_iterator<iterator> reverse_iterator;
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+  inline iterator
+  begin()
+  {
+    if (unlikely(large_elems))
+      return iterator(large_elems->begin());
+    return iterator(small_begin());
+  }
+
+  inline const_iterator
+  begin() const
+  {
+    if (unlikely(large_elems))
+      return const_iterator(large_elems->begin());
+    return const_iterator(small_begin());
+  }
+
+  inline iterator
+  end()
+  {
+    if (unlikely(large_elems))
+      return iterator(large_elems->end());
+    return iterator(small_end());
+  }
+
+  inline const_iterator
+  end() const
+  {
+    if (unlikely(large_elems))
+      return const_iterator(large_elems->end());
+    return const_iterator(small_end());
+  }
+
+  inline reverse_iterator
+  rbegin()
+  {
+    return reverse_iterator(end());
+  }
+
+  inline const_reverse_iterator
+  rbegin() const
+  {
+    return const_reverse_iterator(end());
+  }
+
+  inline reverse_iterator
+  rend()
+  {
+    return reverse_iterator(begin());
+  }
+
+  inline const_reverse_iterator
+  rend() const
+  {
+    return const_reverse_iterator(begin());
+  }
+
+private:
+  void
+  assignFrom(const small_vector &that)
+  {
+    if (unlikely(this == &that))
+      return;
+    clearDestructive();
+    if (unlikely(that.large_elems)) {
+      large_elems = new large_vector_type(*that.large_elems);
+    } else {
+      INVARIANT(that.n <= SmallSize);
+      if (is_trivially_copyable) {
+        NDB_MEMCPY(ptr(), that.ptr(), that.n * sizeof(T));
+      } else {
+        for (size_t i = 0; i < that.n; i++)
+          new (&(ptr()[i])) T(that.ptr()[i]);
+      }
+      n = that.n;
+    }
+  }
+
+  inline ALWAYS_INLINE T *
+  ptr()
+  {
+    return reinterpret_cast<T *>(&small_elems_buf[0]);
+  }
+
+  inline ALWAYS_INLINE const T *
+  ptr() const
+  {
+    return reinterpret_cast<const T *>(&small_elems_buf[0]);
+  }
+
+  size_t n;
+  char small_elems_buf[sizeof(T) * SmallSize];
+  large_vector_type *large_elems;
+};
+
+#endif /* _SMALL_VECTOR_H_ */
diff --git a/silo/spinbarrier.h b/silo/spinbarrier.h
new file mode 100644 (file)
index 0000000..966f880
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef _SPINBARRIER_H_
+#define _SPINBARRIER_H_
+
+#include "amd64.h"
+#include "macros.h"
+#include "util.h"
+
+/**
+ * Barrier implemented by spinning
+ */
+
+class spin_barrier {
+public:
+  spin_barrier(size_t n)
+    : n(n)
+  {
+    ALWAYS_ASSERT(n > 0);
+  }
+
+  spin_barrier(const spin_barrier &) = delete;
+  spin_barrier(spin_barrier &&) = delete;
+  spin_barrier &operator=(const spin_barrier &) = delete;
+
+  ~spin_barrier()
+  {
+    ALWAYS_ASSERT(n == 0);
+  }
+
+  void
+  count_down()
+  {
+    // written like this (instead of using __sync_fetch_and_add())
+    // so we can have assertions
+    for (;;) {
+      size_t copy = n;
+      ALWAYS_ASSERT(copy > 0);
+      if (__sync_bool_compare_and_swap(&n, copy, copy - 1))
+        return;
+    }
+  }
+
+  void
+  wait_for()
+  {
+    while (n > 0)
+      nop_pause();
+  }
+
+private:
+  volatile size_t n;
+};
+
+#endif /* _SPINBARRIER_H_ */
diff --git a/silo/spinlock.h b/silo/spinlock.h
new file mode 100644 (file)
index 0000000..8c3fd87
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _SPINLOCK_H_
+#define _SPINLOCK_H_
+
+#include <stdint.h>
+
+#include "amd64.h"
+#include "macros.h"
+#include "util.h"
+
+class spinlock {
+public:
+  spinlock() : value(0) {}
+
+  spinlock(const spinlock &) = delete;
+  spinlock(spinlock &&) = delete;
+  spinlock &operator=(const spinlock &) = delete;
+
+  inline void
+  lock()
+  {
+    // XXX: implement SPINLOCK_BACKOFF
+    uint32_t v = value;
+    while (v || !__sync_bool_compare_and_swap(&value, 0, 1)) {
+      nop_pause();
+      v = value;
+    }
+    COMPILER_MEMORY_FENCE;
+  }
+
+  inline bool
+  try_lock()
+  {
+    return __sync_bool_compare_and_swap(&value, 0, 1);
+  }
+
+  inline void
+  unlock()
+  {
+    INVARIANT(value);
+    value = 0;
+    COMPILER_MEMORY_FENCE;
+  }
+
+  // just for debugging
+  inline bool
+  is_locked() const
+  {
+    return value;
+  }
+
+private:
+  volatile uint32_t value;
+};
+
+#endif /* _SPINLOCK_H_ */
diff --git a/silo/static_unordered_map.h b/silo/static_unordered_map.h
new file mode 100644 (file)
index 0000000..e3919c7
--- /dev/null
@@ -0,0 +1,345 @@
+#ifndef _STATIC_UNORDERED_MAP_H_
+#define _STATIC_UNORDERED_MAP_H_
+
+#include "small_unordered_map.h"
+
+/**
+ * XXX(stephentu): allow custom allocator
+ */
+template <typename Key,
+          typename T,
+          size_t StaticSize = SMALL_SIZE_MAP,
+          typename Hash = private_::myhash<Key>>
+class static_unordered_map {
+public:
+  typedef Key key_type;
+  typedef T mapped_type;
+  typedef std::pair<const key_type, mapped_type> value_type;
+  typedef Hash hasher;
+  typedef T & reference;
+  typedef const T & const_reference;
+
+private:
+  typedef std::unordered_map<Key, T, Hash> large_table_type;
+  typedef std::pair<key_type, mapped_type> bucket_value_type;
+
+  static const bool is_trivially_destructible =
+    private_::is_trivially_destructible<bucket_value_type>::value;
+
+  static const size_t TableSize = private_::TableSize(StaticSize);
+  static_assert(StaticSize >= 1, "XXX");
+  static_assert(TableSize >= 1, "XXX");
+
+  struct bucket {
+    inline ALWAYS_INLINE bucket_value_type *
+    ptr()
+    {
+      return reinterpret_cast<bucket_value_type *>(&buf[0]);
+    }
+
+    inline ALWAYS_INLINE const bucket_value_type *
+    ptr() const
+    {
+      return reinterpret_cast<const bucket_value_type *>(&buf[0]);
+    }
+
+    inline ALWAYS_INLINE bucket_value_type &
+    ref()
+    {
+      return *ptr();
+    }
+
+    inline ALWAYS_INLINE const bucket_value_type &
+    ref() const
+    {
+      return *ptr();
+    }
+
+    template <class... Args>
+    inline ALWAYS_INLINE void
+    construct(size_t hash, Args &&... args)
+    {
+      h = hash;
+      new (&ref()) bucket_value_type(std::forward<Args>(args)...);
+    }
+
+    inline ALWAYS_INLINE void
+    destroy()
+    {
+      if (!is_trivially_destructible)
+        ref().~bucket_value_type();
+    }
+
+    struct bucket *bnext;
+    size_t h;
+    char buf[sizeof(value_type)];
+  };
+
+  // iterators are not stable across mutation
+  template <typename BucketType, typename ValueType>
+  class iterator_ : public std::iterator<std::forward_iterator_tag, ValueType> {
+    friend class static_unordered_map;
+  public:
+    inline iterator_() : b(0) {}
+
+    template <typename B, typename V>
+    inline iterator_(const iterator_<B, V> &other)
+      : b(other.b)
+    {}
+
+    inline ValueType &
+    operator*() const
+    {
+      return reinterpret_cast<ValueType &>(b->ref());
+    }
+
+    inline ValueType *
+    operator->() const
+    {
+      return reinterpret_cast<ValueType *>(b->ptr());
+    }
+
+    inline bool
+    operator==(const iterator_ &o) const
+    {
+      return b == o.b;
+    }
+
+    inline bool
+    operator!=(const iterator_ &o) const
+    {
+      return !operator==(o);
+    }
+
+    inline iterator_ &
+    operator++()
+    {
+      b++;
+      return *this;
+    }
+
+    inline iterator_
+    operator++(int)
+    {
+      iterator_ cur = *this;
+      ++(*this);
+      return cur;
+    }
+
+  protected:
+    inline iterator_(BucketType *b)
+      : b(b)
+    {
+    }
+
+  private:
+    BucketType *b;
+  };
+
+  static size_t
+  chain_length(bucket *b)
+  {
+    size_t ret = 0;
+    while (b) {
+      ret++;
+      b = b->bnext;
+    }
+    return ret;
+  }
+
+public:
+
+  typedef iterator_<bucket, value_type> iterator;
+  typedef iterator_<const bucket, const value_type> const_iterator;
+
+  static_unordered_map()
+    : n(0)
+  {
+    NDB_MEMSET(&table[0], 0, sizeof(table));
+  }
+
+  ~static_unordered_map()
+  {
+    for (size_t i = 0; i < n; i++)
+      elems[i].destroy();
+#ifdef ENABLE_EVENT_COUNTERS
+    size_t ml = 0;
+    for (size_t i = 0; i < TableSize; i++) {
+      const size_t l = chain_length(table[i]);
+      ml = std::max(ml, l);
+
+    }
+    if (ml)
+      private_::evt_avg_max_unordered_map_chain_length.offer(ml);
+#endif
+  }
+
+  static_unordered_map(const static_unordered_map &other)
+    : n(0)
+  {
+    NDB_MEMSET(&table[0], 0, sizeof(table));
+    assignFrom(other);
+  }
+
+  static_unordered_map &
+  operator=(const static_unordered_map &other)
+  {
+    // self assignment
+    if (unlikely(this == &other))
+      return *this;
+    assignFrom(other);
+    return *this;
+  }
+
+private:
+  bucket *
+  find_bucket(const key_type &k, size_t *hash_value)
+  {
+    const size_t h = Hash()(k);
+    if (hash_value)
+      *hash_value = h;
+    const size_t i = h % TableSize;
+    bucket *b = table[i];
+    while (b) {
+      const bool check_hash = private_::is_eq_expensive<key_type>::value;
+      if ((!check_hash || b->h == h) && b->ref().first == k)
+        return b;
+      b = b->bnext;
+    }
+    return 0;
+  }
+
+  inline ALWAYS_INLINE const bucket *
+  find_bucket(const key_type &k, size_t *hash_value) const
+  {
+    return const_cast<static_unordered_map *>(this)->find_bucket(k, hash_value);
+  }
+
+public:
+
+  // XXX(stephentu): template away this stuff
+
+  mapped_type &
+  operator[](const key_type &k)
+  {
+    size_t h;
+    bucket *b = find_bucket(k, &h);
+    if (b)
+      return b->ref().second;
+    INVARIANT(n < StaticSize);
+    b = &elems[n++];
+    b->construct(h, k, mapped_type());
+    const size_t i = h % TableSize;
+    b->bnext = table[i];
+    table[i] = b;
+    return b->ref().second;
+  }
+
+  mapped_type &
+  operator[](key_type &&k)
+  {
+    size_t h;
+    bucket *b = find_bucket(k, &h);
+    if (b)
+      return b->ref().second;
+    INVARIANT(n < StaticSize);
+    b = &elems[n++];
+    b->construct(h, std::move(k), mapped_type());
+    const size_t i = h % TableSize;
+    b->bnext = table[i];
+    table[i] = b;
+    return b->ref().second;
+  }
+
+  inline size_t
+  size() const
+  {
+    return n;
+  }
+
+  inline bool
+  empty() const
+  {
+    return !n;
+  }
+
+  iterator
+  begin()
+  {
+    return iterator(&elems[0]);
+  }
+
+  const_iterator
+  begin() const
+  {
+    return const_iterator(&elems[0]);
+  }
+
+  inline iterator
+  end()
+  {
+    return iterator(&elems[n]);
+  }
+
+  inline const_iterator
+  end() const
+  {
+    return const_iterator(&elems[n]);
+  }
+
+  iterator
+  find(const key_type &k)
+  {
+    bucket * const b = find_bucket(k, 0);
+    if (b)
+      return iterator(b);
+    return end();
+  }
+
+  const_iterator
+  find(const key_type &k) const
+  {
+    const bucket * const b = find_bucket(k, 0);
+    if (b)
+      return const_iterator(b);
+    return end();
+  }
+
+  void
+  clear()
+  {
+    if (!n)
+      return;
+    NDB_MEMSET(&table[0], 0, sizeof(table));
+    for (size_t i = 0; i < n; i++)
+      elems[i].destroy();
+    n = 0;
+  }
+
+public:
+  // non-standard API
+  inline bool is_small_type() const { return true; }
+
+private:
+
+  // doesn't check for self assignment
+  inline void
+  assignFrom(const static_unordered_map &that)
+  {
+    clear();
+    for (size_t i = 0; i < that.n; i++) {
+      bucket * const b = &elems[n++];
+      const bucket * const that_b = &that.elems[i];
+      b->construct(that_b->h, that_b->ref().first, that_b->ref().second);
+      const size_t idx = b->h % TableSize;
+      b->bnext = table[idx];
+      table[idx] = b;
+    }
+  }
+
+  size_t n;
+  bucket elems[StaticSize];
+  bucket *table[TableSize];
+};
+
+#endif /* _STATIC_UNORDERED_MAP_H_ */
diff --git a/silo/static_vector.h b/silo/static_vector.h
new file mode 100644 (file)
index 0000000..a515710
--- /dev/null
@@ -0,0 +1,407 @@
+#ifndef _STATIC_VECTOR_H_
+#define _STATIC_VECTOR_H_
+
+#include <algorithm>
+#include <type_traits>
+#include "macros.h"
+#include "ndb_type_traits.h"
+
+template <typename T, size_t StaticSize = SMALL_SIZE_VEC>
+class static_vector {
+
+  static const bool is_trivially_destructible =
+    private_::is_trivially_destructible<T>::value;
+
+  // std::is_trivially_copyable not supported in g++-4.7
+  static const bool is_trivially_copyable = std::is_scalar<T>::value;
+
+public:
+
+  typedef T value_type;
+  typedef T & reference;
+  typedef const T & const_reference;
+  typedef size_t size_type;
+
+  inline static_vector() : n(0) {}
+  inline ~static_vector() { clear(); }
+
+  inline static_vector(const static_vector &that)
+    : n(0)
+  {
+    assignFrom(that);
+  }
+
+  // not efficient, don't use in performance critical parts
+  static_vector(std::initializer_list<T> l)
+    : n(0)
+  {
+    for (auto &p : l)
+      push_back(p);
+  }
+
+  static_vector &
+  operator=(const static_vector &that)
+  {
+    assignFrom(that);
+    return *this;
+  }
+
+  inline size_t
+  size() const
+  {
+    return n;
+  }
+
+  inline bool
+  empty() const
+  {
+    return !n;
+  }
+
+  inline reference
+  front()
+  {
+    INVARIANT(n > 0);
+    INVARIANT(n <= StaticSize);
+    return *ptr();
+  }
+
+  inline const_reference
+  front() const
+  {
+    return const_cast<static_vector *>(this)->front();
+  }
+
+  inline reference
+  back()
+  {
+    INVARIANT(n > 0);
+    INVARIANT(n <= StaticSize);
+    return ptr()[n - 1];
+  }
+
+  inline const_reference
+  back() const
+  {
+    return const_cast<static_vector *>(this)->back();
+  }
+
+  inline void
+  pop_back()
+  {
+    INVARIANT(n > 0);
+    if (!is_trivially_destructible)
+      ptr()[n - 1].~T();
+    n--;
+  }
+
+  inline void
+  push_back(const T &obj)
+  {
+    emplace_back(obj);
+  }
+
+  inline void
+  push_back(T &&obj)
+  {
+    emplace_back(std::move(obj));
+  }
+
+  // C++11 goodness- a strange syntax this is
+
+  template <class... Args>
+  inline void
+  emplace_back(Args &&... args)
+  {
+    INVARIANT(n < StaticSize);
+    new (&(ptr()[n++])) T(std::forward<Args>(args)...);
+  }
+
+  inline reference
+  operator[](int i)
+  {
+    return ptr()[i];
+  }
+
+  inline const_reference
+  operator[](int i) const
+  {
+    return const_cast<static_vector *>(this)->operator[](i);
+  }
+
+  void
+  clear()
+  {
+    if (!is_trivially_destructible)
+      for (size_t i = 0; i < n; i++)
+        ptr()[i].~T();
+    n = 0;
+  }
+
+  inline void
+  reserve(size_t n)
+  {
+  }
+
+  inline void
+  resize(size_t n, value_type val = value_type())
+  {
+    INVARIANT(n <= StaticSize);
+    if (n > this->n) {
+      // expand
+      while (this->n < n)
+        new (&ptr()[this->n++]) T(val);
+    } else if (n < this->n) {
+      // shrink
+      while (this->n > n) {
+        if (!is_trivially_destructible)
+          ptr()[this->n - 1].~T();
+        this->n--;
+      }
+    }
+  }
+
+  // non-standard API
+  inline bool is_small_type() const { return true; }
+
+  template <typename Compare = std::less<T>>
+  inline void
+  sort(Compare c = Compare())
+  {
+    std::sort(begin(), end(), c);
+  }
+
+private:
+
+  template <typename ObjType>
+  class iterator_ : public std::iterator<std::bidirectional_iterator_tag, ObjType> {
+    friend class static_vector;
+  public:
+    inline iterator_() : p(0) {}
+
+    template <typename O>
+    inline iterator_(const iterator_<O> &other)
+      : p(other.p)
+    {}
+
+    inline ObjType &
+    operator*() const
+    {
+      return *p;
+    }
+
+    inline ObjType *
+    operator->() const
+    {
+      return p;
+    }
+
+    inline bool
+    operator==(const iterator_ &o) const
+    {
+      return p == o.p;
+    }
+
+    inline bool
+    operator!=(const iterator_ &o) const
+    {
+      return !operator==(o);
+    }
+
+    inline bool
+    operator<(const iterator_ &o) const
+    {
+      return p < o.p;
+    }
+
+    inline bool
+    operator>=(const iterator_ &o) const
+    {
+      return !operator<(o);
+    }
+
+    inline bool
+    operator>(const iterator_ &o) const
+    {
+      return p > o.p;
+    }
+
+    inline bool
+    operator<=(const iterator_ &o) const
+    {
+      return !operator>(o);
+    }
+
+    inline iterator_ &
+    operator+=(int n)
+    {
+      p += n;
+      return *this;
+    }
+
+    inline iterator_ &
+    operator-=(int n)
+    {
+      p -= n;
+      return *this;
+    }
+
+    inline iterator_
+    operator+(int n) const
+    {
+      iterator_ cpy = *this;
+      return cpy += n;
+    }
+
+    inline iterator_
+    operator-(int n) const
+    {
+      iterator_ cpy = *this;
+      return cpy -= n;
+    }
+
+    inline intptr_t
+    operator-(const iterator_ &o) const
+    {
+      return p - o.p;
+    }
+
+    inline iterator_ &
+    operator++()
+    {
+      ++p;
+      return *this;
+    }
+
+    inline iterator_
+    operator++(int)
+    {
+      iterator_ cur = *this;
+      ++(*this);
+      return cur;
+    }
+
+    inline iterator_ &
+    operator--()
+    {
+      --p;
+      return *this;
+    }
+
+    inline iterator_
+    operator--(int)
+    {
+      iterator_ cur = *this;
+      --(*this);
+      return cur;
+    }
+
+  protected:
+    inline iterator_(ObjType *p) : p(p) {}
+
+  private:
+    ObjType *p;
+  };
+
+public:
+
+  typedef iterator_<T> iterator;
+  typedef iterator_<const T> const_iterator;
+
+  typedef std::reverse_iterator<iterator> reverse_iterator;
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+  inline iterator
+  begin()
+  {
+    return iterator(ptr());
+  }
+
+  inline const_iterator
+  begin() const
+  {
+    return const_iterator(ptr());
+  }
+
+  inline iterator
+  end()
+  {
+    return iterator(endptr());
+  }
+
+  inline const_iterator
+  end() const
+  {
+    return const_iterator(endptr());
+  }
+
+  inline reverse_iterator
+  rbegin()
+  {
+    return reverse_iterator(end());
+  }
+
+  inline const_reverse_iterator
+  rbegin() const
+  {
+    return const_reverse_iterator(end());
+  }
+
+  inline reverse_iterator
+  rend()
+  {
+    return reverse_iterator(begin());
+  }
+
+  inline const_reverse_iterator
+  rend() const
+  {
+    return const_reverse_iterator(begin());
+  }
+
+private:
+  void
+  assignFrom(const static_vector &that)
+  {
+    if (unlikely(this == &that))
+      return;
+    clear();
+    INVARIANT(that.n <= StaticSize);
+    if (is_trivially_copyable) {
+      NDB_MEMCPY(ptr(), that.ptr(), that.n * sizeof(T));
+    } else {
+      for (size_t i = 0; i < that.n; i++)
+        new (&(ptr()[i])) T(that.ptr()[i]);
+    }
+    n = that.n;
+  }
+
+  inline ALWAYS_INLINE T *
+  ptr()
+  {
+    return reinterpret_cast<T *>(&elems_buf[0]);
+  }
+
+  inline ALWAYS_INLINE const T *
+  ptr() const
+  {
+    return reinterpret_cast<const T *>(&elems_buf[0]);
+  }
+
+  inline ALWAYS_INLINE T *
+  endptr()
+  {
+    return ptr() + n;
+  }
+
+  inline ALWAYS_INLINE const T *
+  endptr() const
+  {
+    return ptr() + n;
+  }
+
+  size_t n;
+  char elems_buf[sizeof(T) * StaticSize];
+};
+
+#endif /* _STATIC_VECTOR_H_ */
diff --git a/silo/stats_client.cc b/silo/stats_client.cc
new file mode 100644 (file)
index 0000000..d35799c
--- /dev/null
@@ -0,0 +1,90 @@
+/**
+ * stats_client.cc
+ *
+ * stand-alone client to poll a stats server
+ *
+ */
+
+#include <iostream>
+#include <string>
+#include <system_error>
+#include <thread>
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "counter.h"
+#include "stats_common.h"
+#include "util.h"
+
+using namespace std;
+using namespace util;
+
+int
+main(int argc, char **argv)
+{
+  if (argc != 3) {
+    cerr << "[usage] " << argv[0] << " sockfile counterspec" << endl;
+    return 1;
+  }
+
+  const string sockfile(argv[1]);
+  const vector<string> counter_names = split(argv[2], ':');
+
+  int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (fd < 0)
+    throw system_error(errno, system_category(),
+        "creating UNIX domain socket");
+  struct sockaddr_un addr;
+  memset(&addr, 0, sizeof addr);
+  addr.sun_family = AF_UNIX;
+  if (sockfile.length() + 1 >= sizeof addr.sun_path)
+    throw range_error("UNIX domain socket path too long");
+  strcpy(addr.sun_path, sockfile.c_str());
+  size_t len = strlen(addr.sun_path) + sizeof(addr.sun_family);
+  if (connect(fd, (struct sockaddr *) &addr, len) < 0)
+    throw system_error(errno, system_category(),
+        "connecting to socket");
+
+  packet pkt;
+  int r;
+  timer loop_timer;
+  for (;;) {
+    for (auto &name : counter_names) {
+      uint8_t buf[1 + name.size()];
+      buf[0] = (uint8_t) stats_command::GET_COUNTER_VALUE;
+      memcpy(&buf[1], name.data(), name.size());
+      pkt.assign((const char *) &buf[0], sizeof(buf));
+      if ((r = pkt.sendpkt(fd))) {
+        perror("send - disconnecting");
+        return 1;
+      }
+      if ((r = pkt.recvpkt(fd))) {
+        if (r == EOF)
+          return 0;
+        perror("recv - disconnecting");
+        return 1;
+      }
+      const get_counter_value_t *resp = (const get_counter_value_t *) pkt.data();
+      cout << name                << " "
+           << resp->timestamp_us_ << " "
+           << resp->d_.count_     << " "
+           << resp->d_.sum_       << " "
+           << resp->d_.max_       << endl;
+    }
+
+    const uint64_t last_loop_usec  = loop_timer.lap();
+    //const uint64_t delay_time_usec = 1000000;
+    const uint64_t delay_time_usec = 1000000/1000;
+    if (last_loop_usec < delay_time_usec) {
+      const uint64_t sleep_ns = (delay_time_usec - last_loop_usec) * 1000;
+      struct timespec t;
+      t.tv_sec  = sleep_ns / ONE_SECOND_NS;
+      t.tv_nsec = sleep_ns % ONE_SECOND_NS;
+      nanosleep(&t, nullptr);
+    }
+  }
+
+  return 0;
+}
diff --git a/silo/stats_common.h b/silo/stats_common.h
new file mode 100644 (file)
index 0000000..55d5b76
--- /dev/null
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "counter.h"
+#include "macros.h"
+#include "fileutils.h"
+
+enum class stats_command : uint8_t { GET_COUNTER_VALUE = 0x1 };
+
+struct get_counter_value_t {
+  uint64_t timestamp_us_; // usec
+  counter_data d_;
+};
+
+class packet {
+public:
+  static const size_t MAX_DATA = 0xFFFF - 4;
+  packet() : size_(0) {}
+  inline void clear() { size_ = 0; }
+  inline void
+  assign(const char *p, size_t n)
+  {
+    INVARIANT(n <= MAX_DATA);
+    NDB_MEMCPY(&data_[0], p, n);
+    size_ = n;
+  }
+  inline void
+  assign(const std::string &s)
+  {
+    assign(s.data(), s.size());
+  }
+  int
+  sendpkt(int fd) const
+  {
+    // XXX: we don't care about endianness
+    return fileutils::writeall(
+        fd, (const char *) &size_, sizeof(size_) + size_);
+  }
+  int
+  recvpkt(int fd)
+  {
+    // XXX: we don't care about endianness
+    int r;
+    if ((r = fileutils::readall(fd, (char *) &size_, sizeof(size_)))) {
+      clear();
+      return r;
+    }
+    if (size_ > packet::MAX_DATA) {
+      std::cerr << "bad packet read with excessive size" << std::endl;
+      clear();
+      return -1;
+    }
+    if ((r = fileutils::readall(fd, &data_[0], size_))) {
+      clear();
+      return r;
+    }
+    return 0;
+  }
+  inline uint32_t size() const { return size_; }
+  inline const char * data() const { return &data_[0]; }
+private:
+  uint32_t size_;
+  char data_[MAX_DATA];
+};
diff --git a/silo/stats_server.cc b/silo/stats_server.cc
new file mode 100644 (file)
index 0000000..bfd4739
--- /dev/null
@@ -0,0 +1,94 @@
+#include <system_error>
+#include <thread>
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "counter.h"
+#include "stats_server.h"
+#include "util.h"
+
+using namespace std;
+using namespace util;
+
+stats_server::stats_server(const string &sockfile)
+  : sockfile_(sockfile) {}
+
+void
+stats_server::serve_forever()
+{
+  int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (fd < 0)
+    throw system_error(errno, system_category(),
+        "creating UNIX domain socket");
+
+  struct sockaddr_un addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sun_family = AF_UNIX;
+  if (sockfile_.length() + 1 >= sizeof(addr.sun_path))
+    throw range_error("UNIX domain socket path too long");
+  strcpy(addr.sun_path, sockfile_.c_str());
+  unlink(sockfile_.c_str());
+
+  if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+    throw system_error(errno, system_category(),
+        "binding to " + sockfile_);
+
+  if (listen(fd, 5) < 0)
+    throw system_error(errno, system_category(),
+        "listening on " + sockfile_);
+
+  for (;;) {
+    int cfd = accept(fd, nullptr, 0);
+    if (cfd < 0)
+      throw system_error(errno, system_category(), "accept failed");
+    thread(&stats_server::serve_client, this, cfd).detach();
+  }
+}
+
+
+bool
+stats_server::handle_cmd_get_counter_value(const string &name, packet &pkt)
+{
+  get_counter_value_t ret;
+  ret.timestamp_us_ = timer::cur_usec();
+  if (!event_counter::stat(name, ret.d_))
+    cerr << "could not find counter " << name << endl;
+  pkt.assign((const char *) &ret, sizeof(ret));
+  return true;
+}
+
+void
+stats_server::serve_client(int fd)
+{
+  packet pkt;
+  string scratch;
+  for (;;) {
+    int r = pkt.recvpkt(fd);
+    if (r == EOF) {
+      cerr << "client disconnected" << endl;
+      return;
+    }
+    if (r) {
+      perror("recv- dropping connection");
+      return;
+    }
+    INVARIANT(pkt.size());
+    switch (pkt.data()[0]) {
+    case static_cast<uint8_t>(stats_command::GET_COUNTER_VALUE):
+      {
+        scratch.assign(pkt.data() + 1, pkt.size() - 1);
+        if (!handle_cmd_get_counter_value(scratch, pkt)) {
+          cerr << "error on handle_cmd_get_counter_value(), dropping" << endl;
+          return;
+        }
+        pkt.sendpkt(fd);
+        break;
+      }
+    default:
+      cerr << "bad command- dropping connection" << endl;
+      return;
+    }
+  }
+}
diff --git a/silo/stats_server.h b/silo/stats_server.h
new file mode 100644 (file)
index 0000000..238789d
--- /dev/null
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <string>
+#include "stats_common.h"
+
+// serves over unix socket
+class stats_server {
+public:
+  stats_server(const std::string &sockfile);
+  void serve_forever(); // blocks current thread
+private:
+  bool handle_cmd_get_counter_value(const std::string &name, packet &pkt);
+  void serve_client(int fd);
+  std::string sockfile_;
+};
diff --git a/silo/str_arena.h b/silo/str_arena.h
new file mode 100644 (file)
index 0000000..c596918
--- /dev/null
@@ -0,0 +1,133 @@
+#pragma once
+
+#include <string>
+#include <memory>
+#include "small_vector.h"
+
+// XXX: str arena hardcoded now to handle at most 1024 strings
+class str_arena {
+public:
+
+  static const size_t PreAllocBufSize = 256;
+  static const size_t NStrs = 1024;
+
+  static const size_t MinStrReserveLength = 2 * CACHELINE_SIZE;
+  static_assert(PreAllocBufSize >= MinStrReserveLength, "xx");
+
+  str_arena()
+    : n(0)
+  {
+    for (size_t i = 0; i < NStrs; i++)
+      strs[i].reserve(PreAllocBufSize);
+  }
+
+  // non-copyable/non-movable for the time being
+  str_arena(str_arena &&) = delete;
+  str_arena(const str_arena &) = delete;
+  str_arena &operator=(const str_arena &) = delete;
+
+  inline void
+  reset()
+  {
+    n = 0;
+    overflow.clear();
+  }
+
+  // next() is guaranteed to return an empty string
+  std::string *
+  next()
+  {
+    if (likely(n < NStrs)) {
+      std::string * const px = &strs[n++];
+      px->clear();
+      INVARIANT(manages(px));
+      return px;
+    }
+    // only loaders need this- and this allows us to use a unified
+    // str_arena for loaders/workers
+    overflow.emplace_back(new std::string);
+    ++n;
+    return overflow.back().get();
+  }
+
+  inline std::string *
+  operator()()
+  {
+    return next();
+  }
+
+  void
+  return_last(std::string *px)
+  {
+    INVARIANT(n > 0);
+    --n;
+  }
+
+  bool
+  manages(const std::string *px) const
+  {
+    return manages_local(px) || manages_overflow(px);
+  }
+
+private:
+
+  bool
+  manages_local(const std::string *px) const
+  {
+    if (px < &strs[0])
+      return false;
+    if (px >= &strs[NStrs])
+      return false;
+    return 0 == ((reinterpret_cast<const char *>(px) -
+                  reinterpret_cast<const char *>(&strs[0])) % sizeof(std::string));
+  }
+
+  bool
+  manages_overflow(const std::string *px) const
+  {
+    for (auto &p : overflow)
+      if (p.get() == px)
+        return true;
+    return false;
+  }
+
+private:
+  std::string strs[NStrs];
+  std::vector<std::unique_ptr<std::string>> overflow;
+  size_t n;
+};
+
+class scoped_str_arena {
+public:
+  scoped_str_arena(str_arena *arena)
+    : arena(arena)
+  {
+  }
+
+  scoped_str_arena(str_arena &arena)
+    : arena(&arena)
+  {
+  }
+
+  scoped_str_arena(scoped_str_arena &&) = default;
+
+  // non-copyable
+  scoped_str_arena(const scoped_str_arena &) = delete;
+  scoped_str_arena &operator=(const scoped_str_arena &) = delete;
+
+
+  ~scoped_str_arena()
+  {
+    if (arena)
+      arena->reset();
+  }
+
+  inline ALWAYS_INLINE str_arena *
+  get()
+  {
+    return arena;
+  }
+
+private:
+  str_arena *arena;
+};
diff --git a/silo/test.cc b/silo/test.cc
new file mode 100644 (file)
index 0000000..2b18490
--- /dev/null
@@ -0,0 +1,1016 @@
+#include <iostream>
+#include <functional>
+#include <unordered_map>
+#include <tuple>
+#include <set>
+#include <unistd.h>
+
+#include "circbuf.h"
+#include "pxqueue.h"
+#include "core.h"
+#include "thread.h"
+#include "txn.h"
+#include "txn_btree.h"
+#include "varint.h"
+#include "small_vector.h"
+#include "static_vector.h"
+#include "small_unordered_map.h"
+#include "static_unordered_map.h"
+#include "counter.h"
+#include "record/encoder.h"
+#include "record/inline_str.h"
+#include "record/cursor.h"
+
+#ifdef PROTO2_CAN_DISABLE_GC
+#include "txn_proto2_impl.h"
+#endif
+
+#define MYREC_KEY_FIELDS(x, y) \
+  x(int32_t,k0) \
+  y(int32_t,k1)
+#define MYREC_VALUE_FIELDS(x, y) \
+  x(int32_t,v0) \
+  y(int16_t,v1)
+DO_STRUCT(myrec, MYREC_KEY_FIELDS, MYREC_VALUE_FIELDS)
+
+#define CURSORREC_KEY_FIELDS(x, y) \
+  x(int32_t,k0) \
+  y(int32_t,k1)
+#define CURSORREC_VALUE_FIELDS(x, y) \
+  x(int32_t,v0) \
+  y(int16_t,v1) \
+  y(int32_t,v2) \
+  y(inline_str_8<25>,v3) \
+  y(inline_str_fixed<25>,v4) \
+  y(int32_t,v5) \
+  y(int8_t,v6)
+DO_STRUCT(cursorrec, CURSORREC_KEY_FIELDS, CURSORREC_VALUE_FIELDS)
+
+using namespace std;
+using namespace util;
+
+static event_counter evt_test("test");
+static event_counter evt_test1("test1");
+static event_avg_counter evt_test_avg("test_avg");
+
+namespace varkeytest {
+  void
+  Test()
+  {
+    const string s0 = u64_varkey(1).str();
+    ALWAYS_ASSERT(s0.size() == 8);
+    const char e0[] = { 0, 0, 0, 0, 0, 0, 0, 1 };
+    ALWAYS_ASSERT(memcmp(s0.data(), &e0[0], 8) == 0);
+    cout << "varkey test passed" << endl;
+  }
+}
+
+namespace pxqueuetest {
+
+  static inline void *
+  N2P(uint64_t n)
+  {
+    return (void *) n;
+  }
+
+  static inline rcu::deleter_t
+  N2D(uint64_t n)
+  {
+    return (rcu::deleter_t) n;
+  }
+
+  struct sorter {
+    bool
+    operator()(const rcu::delete_entry &a,
+               const rcu::delete_entry &b) const
+    {
+      return (a.ptr < b.ptr) || (a.ptr == b.ptr && a.action < b.action);
+    }
+  };
+
+  void
+  Test()
+  {
+    typedef basic_px_queue<rcu::delete_entry, 8> px_queue;
+
+    {
+      px_queue q0;
+      for (size_t i = 0; i < 14; i++)
+        q0.enqueue(rcu::delete_entry(N2P(i), N2D(i)), 1);
+      ALWAYS_ASSERT(!q0.empty());
+      q0.sanity_check();
+
+      uint64_t i = 0;
+      for (auto &p : q0) {
+        ALWAYS_ASSERT(p.ptr == N2P(i));
+        ALWAYS_ASSERT((rcu::deleter_t) p.action == N2D(i));
+        i++;
+      }
+      ALWAYS_ASSERT(i == 14);
+
+      px_queue q1;
+      q1.enqueue(rcu::delete_entry(N2P(123), N2D(543)), 1);
+      q1.enqueue(rcu::delete_entry(N2P(12345), N2D(54321)), 1);
+      q1.sanity_check();
+      auto q1_it = q1.begin();
+      ALWAYS_ASSERT(q1_it != q1.end());
+      ALWAYS_ASSERT(q1_it->ptr == N2P(123));
+      ALWAYS_ASSERT((rcu::deleter_t) q1_it->action == N2D(543));
+      ++q1_it;
+      ALWAYS_ASSERT(q1_it != q1.end());
+      ALWAYS_ASSERT(q1_it->ptr == N2P(12345));
+      ALWAYS_ASSERT((rcu::deleter_t) q1_it->action == N2D(54321));
+      ++q1_it;
+      ALWAYS_ASSERT(q1_it == q1.end());
+    }
+
+    {
+      px_queue q0, q1;
+      q1.enqueue(rcu::delete_entry(N2P(1), N2D(1)), 1);
+      q1.enqueue(rcu::delete_entry(N2P(2), N2D(2)), 2);
+
+      q0.sanity_check();
+      q1.sanity_check();
+
+      q0.empty_accept_from(q1, 1);
+      ALWAYS_ASSERT(q0.get_ngroups() == 1);
+      ALWAYS_ASSERT(q1.get_ngroups() == 1);
+
+      vector<rcu::delete_entry> v0(q0.begin(), q0.end());
+      vector<rcu::delete_entry> v0test = {{N2P(1), N2D(1)}};
+
+      vector<rcu::delete_entry> v1(q1.begin(), q1.end());
+      vector<rcu::delete_entry> v1test = {{N2P(2), N2D(2)}};
+
+      sort(v0.begin(), v0.end(), sorter());
+      sort(v0test.begin(), v0test.end(), sorter());
+      sort(v1.begin(), v1.end(), sorter());
+      sort(v1test.begin(), v1test.end(), sorter());
+
+      ALWAYS_ASSERT(v0 == v0test);
+      ALWAYS_ASSERT(v1 == v1test);
+
+      q0.clear();
+      ALWAYS_ASSERT(q0.empty());
+      q0.sanity_check();
+
+      q1.clear();
+      ALWAYS_ASSERT(q1.empty());
+      q1.sanity_check();
+    }
+
+    {
+      px_queue q0, q1;
+      q0.enqueue(rcu::delete_entry(N2P(1), N2D(1)), 1);
+      q0.enqueue(rcu::delete_entry(N2P(3), N2D(3)), 1);
+
+      q1.enqueue(rcu::delete_entry(N2P(2), N2D(2)), 1);
+      q1.enqueue(rcu::delete_entry(N2P(4), N2D(4)), 1);
+      q1.enqueue(rcu::delete_entry(N2P(6), N2D(6)), 2);
+
+      q0.sanity_check();
+      q1.sanity_check();
+
+      q0.transfer_freelist(q1);
+      q0.sanity_check();
+      q1.sanity_check();
+
+      q1.transfer_freelist(q0);
+      q0.sanity_check();
+      q1.sanity_check();
+
+      q0.clear();
+      ALWAYS_ASSERT(q0.empty());
+      q1.clear();
+      ALWAYS_ASSERT(q1.empty());
+      q0.sanity_check();
+      q1.sanity_check();
+    }
+
+    cout << "pxqueue test passed" << endl;
+  }
+}
+
+void
+CircbufTest()
+{
+  // test circbuf
+  int values[] = {0, 1, 2, 3, 4};
+  circbuf<int, ARRAY_NELEMS(values)> b;
+  ALWAYS_ASSERT(b.empty());
+  for (size_t i = 0; i < ARRAY_NELEMS(values); i++) {
+    b.enq(&values[i]);
+  }
+  vector<int *> pxs;
+  b.peekall(pxs);
+  ALWAYS_ASSERT(pxs.size() == ARRAY_NELEMS(values));
+  ALWAYS_ASSERT(set<int *>(pxs.begin(), pxs.end()).size() == pxs.size());
+  for (size_t i = 0; i < ARRAY_NELEMS(values); i++)
+    ALWAYS_ASSERT(pxs[i] == &values[i]);
+  for (size_t i = 0; i < ARRAY_NELEMS(values); i++) {
+    ALWAYS_ASSERT(!b.empty());
+    ALWAYS_ASSERT(b.peek() == &values[i]);
+    ALWAYS_ASSERT(*b.peek() == values[i]);
+    ALWAYS_ASSERT(b.deq() == &values[i]);
+  }
+  ALWAYS_ASSERT(b.empty());
+
+  b.enq(&values[0]);
+  b.enq(&values[1]);
+  b.enq(&values[2]);
+  b.peekall(pxs);
+  auto testlist = vector<int *>({&values[0], &values[1], &values[2]});
+  ALWAYS_ASSERT(pxs == testlist);
+
+  ALWAYS_ASSERT(b.deq() == &values[0]);
+  ALWAYS_ASSERT(b.deq() == &values[1]);
+  ALWAYS_ASSERT(b.deq() == &values[2]);
+
+  cout << "circbuf test passed" << endl;
+}
+
+void
+CounterTest()
+{
+#ifdef ENABLE_EVENT_COUNTERS
+  ++evt_test;
+  ++evt_test;
+  evt_test_avg.offer(1);
+  evt_test_avg.offer(2);
+  evt_test_avg.offer(3);
+  map<string, counter_data> m = event_counter::get_all_counters();
+  ALWAYS_ASSERT(m.find("test") != m.end());
+  ALWAYS_ASSERT(m.find("test1") != m.end());
+  ALWAYS_ASSERT(m.find("test_avg") != m.end());
+
+  ALWAYS_ASSERT(m["test"].count_ == 2);
+  ALWAYS_ASSERT(m["test1"].count_ == 0);
+  ALWAYS_ASSERT(m["test_avg"].count_ == 3);
+  ALWAYS_ASSERT(m["test_avg"].sum_ == 6);
+  ALWAYS_ASSERT(m["test_avg"].max_ == 3);
+
+  cout << "event counters test passed" << endl;
+#endif
+}
+
+void
+UtilTest()
+{
+  static_assert(CACHELINE_SIZE == 64, "xx");
+  static_assert(LG_CACHELINE_SIZE == 6, "xx");
+
+  const bool e0 = round_up<size_t, 1>(3) == 4;
+  ALWAYS_ASSERT(e0);
+  const bool e1 = round_down<size_t, 1>(3) == 2;
+  ALWAYS_ASSERT(e1);
+  const bool e2 = round_up<size_t, 1>(2) == 2;
+  ALWAYS_ASSERT(e2);
+  const bool e3 = round_down<size_t, 1>(2) == 2;
+  ALWAYS_ASSERT(e3);
+  const bool e4 = round_down<size_t, LG_CACHELINE_SIZE>(2) == 0;
+  ALWAYS_ASSERT(e4);
+  const bool e5 = round_down<size_t, LG_CACHELINE_SIZE>(CACHELINE_SIZE) == CACHELINE_SIZE;
+  ALWAYS_ASSERT(e5);
+  const bool e6 = round_down<size_t, LG_CACHELINE_SIZE>(CACHELINE_SIZE + 10) == CACHELINE_SIZE;
+  ALWAYS_ASSERT(e6);
+
+  cout << "util test passed" << endl;
+}
+
+namespace small_vector_ns {
+
+typedef small_vector<string, 4> vec_type;
+typedef static_vector<string, 4> static_vec_type;
+typedef vector<string> stl_vec_type;
+
+template <typename VecType>
+static void
+init_vec0(VecType &v)
+{
+  ALWAYS_ASSERT(v.empty());
+  ALWAYS_ASSERT(v.size() == 0);
+
+  v.push_back("a");
+  ALWAYS_ASSERT(!v.empty());
+  ALWAYS_ASSERT(v.size() == 1);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "a");
+
+  v.push_back("b");
+  ALWAYS_ASSERT(v.size() == 2);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "b");
+
+  v.push_back("c");
+  ALWAYS_ASSERT(v.size() == 3);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "c");
+
+  v.push_back("d");
+  ALWAYS_ASSERT(v.size() == 4);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "d");
+}
+
+template <typename VecType>
+static void
+init_vec1(VecType &v)
+{
+  ALWAYS_ASSERT(v.empty());
+  ALWAYS_ASSERT(v.size() == 0);
+
+  v.push_back("a");
+  ALWAYS_ASSERT(!v.empty());
+  ALWAYS_ASSERT(v.size() == 1);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "a");
+
+  v.push_back("b");
+  ALWAYS_ASSERT(v.size() == 2);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "b");
+
+  v.push_back("c");
+  ALWAYS_ASSERT(v.size() == 3);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "c");
+
+  v.push_back("d");
+  ALWAYS_ASSERT(v.size() == 4);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "d");
+
+  v.push_back("e");
+  ALWAYS_ASSERT(v.size() == 5);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "e");
+
+  v.push_back("f");
+  ALWAYS_ASSERT(v.size() == 6);
+  ALWAYS_ASSERT(v.front() == "a");
+  ALWAYS_ASSERT(v.back() == "f");
+}
+
+template <typename VecA, typename VecB>
+static void
+assert_vecs_equal(VecA &v, const VecB &stl_v)
+{
+  ALWAYS_ASSERT(v.size() == stl_v.size());
+  VecB tmp(v.begin(), v.end());
+  ALWAYS_ASSERT(tmp == stl_v);
+  const VecA &cv = v;
+  VecB tmp1(cv.begin(), cv.end());
+  ALWAYS_ASSERT(tmp1 == stl_v);
+}
+
+struct foo {
+  int a;
+  int b;
+  int c;
+  int d;
+
+  foo()
+    : a(), b(), c(), d()
+  {}
+
+  foo(int a, int b, int c, int d)
+    : a(a), b(b), c(c), d(d)
+  {}
+};
+
+struct PComp {
+  inline bool
+  operator()(const pair<uint32_t, uint32_t> &a,
+             const pair<uint32_t, uint32_t> &b) const
+  {
+    return a.first < b.first;
+  }
+};
+
+void
+Test()
+{
+  {
+    vec_type v;
+    static_vec_type static_v;
+    stl_vec_type stl_v;
+    init_vec0(v);
+    init_vec0(static_v);
+    init_vec0(stl_v);
+
+    vec_type v_copy(v);
+    vec_type v_assign;
+    ALWAYS_ASSERT(v_assign.empty());
+    v_assign = v;
+
+    static_vec_type static_v_copy(static_v);
+    static_vec_type static_v_assign;
+    ALWAYS_ASSERT(static_v_assign.empty());
+    static_v_assign = static_v;
+
+    assert_vecs_equal(v, stl_v);
+    assert_vecs_equal(v_copy, stl_v);
+    assert_vecs_equal(v_assign, stl_v);
+
+    assert_vecs_equal(static_v, stl_v);
+    assert_vecs_equal(static_v_copy, stl_v);
+    assert_vecs_equal(static_v_assign, stl_v);
+
+    v.clear();
+    assert_vecs_equal(v, stl_vec_type());
+
+    static_v.clear();
+    assert_vecs_equal(static_v, stl_vec_type());
+  }
+
+  {
+    vec_type v;
+    stl_vec_type stl_v;
+    init_vec1(v);
+    init_vec1(stl_v);
+    vec_type v_copy(v);
+    vec_type v_assign;
+    ALWAYS_ASSERT(v_assign.empty());
+    v_assign = v;
+    assert_vecs_equal(v, stl_v);
+    assert_vecs_equal(v_copy, stl_v);
+    assert_vecs_equal(v_assign, stl_v);
+    v.clear();
+    assert_vecs_equal(v, stl_vec_type());
+  }
+
+  {
+    for (int iter = 0; iter < 10; iter++) {
+      small_vector<foo> v;
+      for (int i = 0; i < 20; i++) {
+        v.push_back(foo(i, i + 1, i + 2, i + 3));
+        ALWAYS_ASSERT(v.back().a == i);
+        ALWAYS_ASSERT(v.back().b == (i + 1));
+        ALWAYS_ASSERT(v.back().c == (i + 2));
+        ALWAYS_ASSERT(v.back().d == (i + 3));
+        ALWAYS_ASSERT(v[i].a == i);
+        ALWAYS_ASSERT(v[i].b == (i + 1));
+        ALWAYS_ASSERT(v[i].c == (i + 2));
+        ALWAYS_ASSERT(v[i].d == (i + 3));
+      }
+      for (int i = 0; i < 20; i++) {
+        ALWAYS_ASSERT(v[i].a == i);
+        ALWAYS_ASSERT(v[i].b == (i + 1));
+        ALWAYS_ASSERT(v[i].c == (i + 2));
+        ALWAYS_ASSERT(v[i].d == (i + 3));
+      }
+    }
+  }
+
+  {
+    small_vector<int> v;
+    v.push_back(10);
+    v.push_back(2);
+    v.push_back(5);
+    v.push_back(7);
+    v.push_back(3);
+    v.push_back(100);
+    sort(v.begin(), v.end());
+
+    small_vector<int> v1;
+    v1.push_back(10);
+    v1.push_back(2);
+    v1.push_back(5);
+    v1.push_back(7);
+    v1.push_back(3);
+    v1.push_back(100);
+    v1.sort();
+
+    vector<int> stl_v;
+    stl_v.push_back(10);
+    stl_v.push_back(2);
+    stl_v.push_back(5);
+    stl_v.push_back(7);
+    stl_v.push_back(3);
+    stl_v.push_back(100);
+    sort(stl_v.begin(), stl_v.end());
+
+    assert_vecs_equal(v, stl_v);
+    assert_vecs_equal(v1, stl_v);
+  }
+
+  {
+    small_vector<int, 3> v;
+    v.push_back(10);
+    v.push_back(2);
+    v.push_back(5);
+    v.push_back(7);
+    v.push_back(3);
+    v.push_back(100);
+    sort(v.begin(), v.end());
+
+    small_vector<int, 3> v1;
+    v1.push_back(10);
+    v1.push_back(2);
+    v1.push_back(5);
+    v1.push_back(7);
+    v1.push_back(3);
+    v1.push_back(100);
+    v1.sort();
+
+    vector<int> stl_v;
+    stl_v.push_back(10);
+    stl_v.push_back(2);
+    stl_v.push_back(5);
+    stl_v.push_back(7);
+    stl_v.push_back(3);
+    stl_v.push_back(100);
+    sort(stl_v.begin(), stl_v.end());
+
+    assert_vecs_equal(v, stl_v);
+    assert_vecs_equal(v1, stl_v);
+  }
+
+  {
+    fast_random r(29395);
+    small_vector< pair<uint32_t, uint32_t> > v;
+    vector< pair<uint32_t, uint32_t> > stl_v;
+    for (size_t i = 0; i < 48; i++) {
+      uint32_t x = r.next();
+      v.push_back(make_pair(x, x + 1));
+      stl_v.push_back(make_pair(x, x + 1));
+    }
+    sort(v.begin(), v.end(), PComp());
+    sort(stl_v.begin(), stl_v.end(), PComp());
+    assert_vecs_equal(v, stl_v);
+    for (size_t i = 0; i < 48; i++)
+      ALWAYS_ASSERT(v[i].first + 1 == v[i].second);
+  }
+
+  {
+    // test C++11 features
+    small_vector<string, 4> v;
+    v.emplace_back("hello");
+    string world = "world";
+    v.push_back(move(world));
+    //ALWAYS_ASSERT(world.empty());
+    ALWAYS_ASSERT(v.size() == 2);
+    ALWAYS_ASSERT(v[0] == "hello");
+    ALWAYS_ASSERT(v[1] == "world");
+  }
+
+  {
+    // reverse iterators
+    vec_type v0;
+    v0.emplace_back("a");
+    v0.emplace_back("b");
+    v0.emplace_back("c");
+    v0.emplace_back("d");
+
+    stl_vec_type sv0;
+    sv0.emplace_back("a");
+    sv0.emplace_back("b");
+    sv0.emplace_back("c");
+    sv0.emplace_back("d");
+
+    stl_vec_type test(v0.rbegin(), v0.rend());
+    stl_vec_type svtest(sv0.rbegin(), sv0.rend());
+    ALWAYS_ASSERT(test == svtest);
+
+    v0.emplace_back("e");
+    sv0.emplace_back("e");
+
+    stl_vec_type test1(v0.rbegin(), v0.rend());
+    stl_vec_type svtest1(sv0.rbegin(), sv0.rend());
+    ALWAYS_ASSERT(test1 == svtest1);
+  }
+
+  cout << "vec test passed" << endl;
+}
+
+}
+
+namespace small_map_ns {
+
+typedef small_unordered_map<string, string, 4> map_type;
+typedef static_unordered_map<string, string, 4> static_map_type;
+typedef map<string, string> stl_map_type;
+
+template <typename MapType>
+static void
+assert_map_contains(MapType &m, const string &k, const string &v)
+{
+  ALWAYS_ASSERT(!m.empty());
+  ALWAYS_ASSERT(m[k] == v);
+  {
+    typename MapType::iterator it = m.find(k);
+    ALWAYS_ASSERT(it != m.end());
+    ALWAYS_ASSERT(it->first == k);
+    ALWAYS_ASSERT(it->second == v);
+  }
+  const MapType &const_m = m;
+  {
+    typename MapType::const_iterator it = const_m.find(k);
+    ALWAYS_ASSERT(it != const_m.end());
+    ALWAYS_ASSERT(it->first == k);
+    ALWAYS_ASSERT(it->second == v);
+  }
+}
+
+template <typename MapType>
+static void
+assert_map_equals(MapType &m, const stl_map_type &stl_m)
+{
+  ALWAYS_ASSERT(m.size() == stl_m.size());
+
+  // reg version prefix
+  {
+    stl_map_type test;
+    for (typename MapType::iterator it = m.begin();
+         it != m.end(); ++it) {
+      ALWAYS_ASSERT(test.find(it->first) == test.end());
+      test[it->first] = it->second;
+    }
+    ALWAYS_ASSERT(test == stl_m);
+  }
+
+  // reg version postfix
+  {
+    stl_map_type test;
+    for (typename MapType::iterator it = m.begin();
+         it != m.end(); it++) {
+      ALWAYS_ASSERT(test.find(it->first) == test.end());
+      test[it->first] = it->second;
+    }
+    ALWAYS_ASSERT(test == stl_m);
+  }
+
+  // const version prefix
+  {
+    const MapType &const_m = m;
+    stl_map_type test;
+    for (typename MapType::const_iterator it = const_m.begin();
+         it != const_m.end(); ++it) {
+      ALWAYS_ASSERT(test.find(it->first) == test.end());
+      test[it->first] = it->second;
+    }
+    ALWAYS_ASSERT(test == stl_m);
+  }
+
+  // const version postfix
+  {
+    const MapType &const_m = m;
+    stl_map_type test;
+    for (typename MapType::const_iterator it = const_m.begin();
+         it != const_m.end(); it++) {
+      ALWAYS_ASSERT(test.find(it->first) == test.end());
+      test[it->first] = it->second;
+    }
+    ALWAYS_ASSERT(test == stl_m);
+  }
+}
+
+template <typename T>
+static void
+init_map(T& m)
+{
+  m["a"] = "1";
+  m["b"] = "2";
+  m["c"] = "3";
+  m["d"] = "4";
+}
+
+template <typename T>
+static void
+init_map1(T& m)
+{
+  m["a"] = "1";
+  m["b"] = "2";
+  m["c"] = "3";
+  m["d"] = "4";
+  m["e"] = "5";
+  m["f"] = "6";
+}
+
+void
+Test()
+{
+  {
+    map_type m, m_copy;
+    static_map_type static_m, static_m_copy;
+    stl_map_type stl_m;
+
+    init_map(m);
+    init_map(static_m);
+
+    ALWAYS_ASSERT(m.is_small_type());
+    ALWAYS_ASSERT(static_m.is_small_type());
+    INVARIANT(m.size() == 4);
+    INVARIANT(static_m.size() == 4);
+
+    init_map(stl_m);
+
+    ALWAYS_ASSERT(m_copy.is_small_type());
+    m_copy = m;
+    ALWAYS_ASSERT(m_copy.is_small_type());
+    INVARIANT(m_copy.size() == 4);
+
+    static_m_copy = static_m;
+    INVARIANT(static_m_copy.size() == 4);
+
+    map_type m_construct(m);
+    INVARIANT(m_construct.size() == 4);
+    for (stl_map_type::iterator it = stl_m.begin();
+         it != stl_m.end(); ++it) {
+      assert_map_contains(m, it->first, it->second);
+      assert_map_contains(m_copy, it->first, it->second);
+      assert_map_contains(m_construct, it->first, it->second);
+    }
+    assert_map_equals(m, stl_m);
+    assert_map_equals(m_copy, stl_m);
+    assert_map_equals(m_construct, stl_m);
+    ALWAYS_ASSERT(m.is_small_type());
+    ALWAYS_ASSERT(m_copy.is_small_type());
+    ALWAYS_ASSERT(m_construct.is_small_type());
+
+    static_map_type static_m_construct(static_m);
+    INVARIANT(static_m_construct.size() == 4);
+    for (stl_map_type::iterator it = stl_m.begin();
+         it != stl_m.end(); ++it) {
+      assert_map_contains(static_m, it->first, it->second);
+      assert_map_contains(static_m_copy, it->first, it->second);
+      assert_map_contains(static_m_construct, it->first, it->second);
+    }
+    assert_map_equals(static_m, stl_m);
+    assert_map_equals(static_m_copy, stl_m);
+    assert_map_equals(static_m_construct, stl_m);
+    ALWAYS_ASSERT(static_m.is_small_type());
+    ALWAYS_ASSERT(static_m_copy.is_small_type());
+    ALWAYS_ASSERT(static_m_construct.is_small_type());
+  }
+
+  {
+    map_type m, m_copy;
+    stl_map_type stl_m;
+    init_map1(m);
+    init_map1(stl_m);
+    m_copy = m;
+    map_type m_construct(m);
+    for (stl_map_type::iterator it = stl_m.begin();
+         it != stl_m.end(); ++it) {
+      assert_map_contains(m, it->first, it->second);
+      assert_map_contains(m_copy, it->first, it->second);
+    }
+    assert_map_equals(m, stl_m);
+    assert_map_equals(m_copy, stl_m);
+    assert_map_equals(m_construct, stl_m);
+  }
+
+  {
+    map_type m;
+    ALWAYS_ASSERT(m.empty());
+    ALWAYS_ASSERT(m.size() == 0);
+    m["a"] = "1";
+    ALWAYS_ASSERT(!m.empty());
+    ALWAYS_ASSERT(m.size() == 1);
+    m["b"] = "2";
+    ALWAYS_ASSERT(!m.empty());
+    ALWAYS_ASSERT(m.size() == 2);
+    m["b"] = "2";
+    ALWAYS_ASSERT(!m.empty());
+    ALWAYS_ASSERT(m.size() == 2);
+    m["c"] = "3";
+    ALWAYS_ASSERT(!m.empty());
+    ALWAYS_ASSERT(m.size() == 3);
+    m["d"] = "4";
+    ALWAYS_ASSERT(!m.empty());
+    ALWAYS_ASSERT(m.size() == 4);
+
+    m.clear();
+    ALWAYS_ASSERT(m.empty());
+    ALWAYS_ASSERT(m.size() == 0);
+    m["a"] = "1";
+    m["b"] = "2";
+    m["c"] = "3";
+    m["d"] = "4";
+    m["d"] = "4";
+    m["d"] = "4";
+    m["e"] = "5";
+    ALWAYS_ASSERT(!m.empty());
+    ALWAYS_ASSERT(m.size() == 5);
+  }
+
+  { // check primitive key type maps
+    small_unordered_map<int, int> m;
+    m[0] = 1;
+    m[1] = 2;
+    m[2] = 3;
+    ALWAYS_ASSERT(m.find(0) != m.end());
+    ALWAYS_ASSERT(m.find(1) != m.end());
+    ALWAYS_ASSERT(m.find(2) != m.end());
+    ALWAYS_ASSERT(m.find(0)->first == 0 && m.find(0)->second == 1);
+    ALWAYS_ASSERT(m.find(1)->first == 1 && m.find(1)->second == 2);
+    ALWAYS_ASSERT(m.find(2)->first == 2 && m.find(2)->second == 3);
+  }
+
+  cout << "map test passed" << endl;
+}
+}
+
+namespace recordtest {
+
+ostream &
+operator<<(ostream &o, const myrec::key &k)
+{
+  o << "[k0=" << k.k0 << ", k1=" << k.k1 << "]" << endl;
+  return o;
+}
+
+ostream &
+operator<<(ostream &o, const myrec::value &v)
+{
+  o << "[v0=" << v.v0 << ", v1=" << v.v1 << "]" << endl;
+  return o;
+}
+
+void
+TestCursor()
+{
+  const cursorrec::value v(12345, 1, 54321, "foo", "bar", 98765, 2);
+  const string enc_v = Encode(v);
+  cursorrec::value v0;
+  read_record_cursor<cursorrec> rc((const uint8_t *) enc_v.data(), enc_v.size());
+  for (size_t i = rc.field();
+       i < cursorrec::value_descriptor::nfields();
+       i = rc.field())
+    rc.read_current_and_advance(&v0);
+  ALWAYS_ASSERT(v == v0);
+
+  // mutate v2 => 98765432, v4 => "asdfasdfasdf"
+  v0.v2 = 98765432;
+  v0.v4.assign("asdfasdfasdf");
+
+  rc.reset();
+  rc.skip_to(2);
+  const size_t v2_oldsz = rc.read_current_raw_size_and_advance();
+  rc.skip_to(4);
+  const size_t v4_oldsz = rc.read_current_raw_size_and_advance();
+
+  typedef serializer<int32_t, true> si32;
+  typedef serializer<inline_str_fixed<25>, true> sistr25;
+  ALWAYS_ASSERT(si32::nbytes(&v.v2) == v2_oldsz);
+  ALWAYS_ASSERT(sistr25::nbytes(&v.v4) == v4_oldsz);
+  ALWAYS_ASSERT(cursorrec::value_descriptor::nbytes_fn(2)((const uint8_t *) &v.v2) == v2_oldsz);
+  ALWAYS_ASSERT(cursorrec::value_descriptor::nbytes_fn(4)((const uint8_t *) &v.v4) == v4_oldsz);
+
+  const size_t v2_newsz =
+    cursorrec::value_descriptor::nbytes_fn(2)((const uint8_t *) &v0.v2);
+  const size_t v4_newsz =
+    cursorrec::value_descriptor::nbytes_fn(4)((const uint8_t *) &v0.v4);
+
+  uint8_t v2_oldraw_v[cursorrec::value_descriptor::max_nbytes(2)];
+  uint8_t v4_oldraw_v[cursorrec::value_descriptor::max_nbytes(4)];
+
+  string enc_v0 = enc_v;
+  if ((v2_oldsz + v4_oldsz) < (v2_newsz + v4_newsz))
+    // grow buffer
+    enc_v0.resize(enc_v0.size() + (v2_newsz + v4_newsz) - (v2_oldsz + v4_oldsz));
+  write_record_cursor<cursorrec> wc((uint8_t *) enc_v0.data());
+  wc.skip_to(2);
+  wc.write_current_and_advance(&v0, &v2_oldraw_v[0]);
+  wc.skip_to(4);
+  wc.write_current_and_advance(&v0, &v4_oldraw_v[0]);
+
+  read_record_cursor<cursorrec> rc1((const uint8_t *) enc_v0.data(), enc_v0.size());
+  cursorrec::value v2;
+  for (size_t i = rc1.field();
+       i < cursorrec::value_descriptor::nfields();
+       i = rc1.field())
+    rc1.read_current_and_advance(&v2);
+  ALWAYS_ASSERT(v2 == v0);
+  cerr << "v2: " << v2 << endl;
+  cerr << "v0: " << v2 << endl;
+}
+
+void
+Test()
+{
+  const myrec::key k0(123, 456);
+  const myrec::key k1(999, 123);
+  const myrec::key k2(123, 457);
+  myrec::key k0_temp, k1_temp, k2_temp;
+
+  ALWAYS_ASSERT(Size(k0) == sizeof(k0));
+  ALWAYS_ASSERT(Size(k1) == sizeof(k1));
+  ALWAYS_ASSERT(Size(k2) == sizeof(k2));
+
+  {
+    const string s0 = Encode(k0);
+    const string s1 = Encode(k1);
+    const string s2 = Encode(k2);
+    ALWAYS_ASSERT(s0 < s1);
+    ALWAYS_ASSERT(s0 < s2);
+    Decode(s0, k0_temp);
+    Decode(s1, k1_temp);
+    Decode(s2, k2_temp);
+    ALWAYS_ASSERT(k0 == k0_temp);
+    ALWAYS_ASSERT(k1 == k1_temp);
+    ALWAYS_ASSERT(k2 == k2_temp);
+
+    string t0, t1, t2;
+    const myrec::key *p0 = Decode(Encode(k0), k0_temp);
+    const myrec::key *p1 = Decode(Encode(k1), k1_temp);
+    const myrec::key *p2 = Decode(Encode(k2), k2_temp);
+    ALWAYS_ASSERT(*p0 == k0);
+    ALWAYS_ASSERT(*p1 == k1);
+    ALWAYS_ASSERT(*p2 == k2);
+  }
+
+  const myrec::value v0(859435, 2834);
+  const myrec::value v1(0, 73);
+  const myrec::value v2(654, 8);
+  myrec::value v0_temp, v1_temp, v2_temp;
+
+  {
+    const size_t sz0 = Size(v0);
+    const size_t sz1 = Size(v1);
+    const size_t sz2 = Size(v2);
+    uint8_t buf0[sz0], buf1[sz1], buf2[sz2];
+    Encode(buf0, v0);
+    Encode(buf1, v1);
+    Encode(buf2, v2);
+
+    const string s0((const char *) buf0, sz0);
+    const string s1((const char *) buf1, sz1);
+    const string s2((const char *) buf2, sz2);
+
+    Decode(s0, v0_temp);
+    Decode(s1, v1_temp);
+    Decode(s2, v2_temp);
+    ALWAYS_ASSERT(v0 == v0_temp);
+    ALWAYS_ASSERT(v1 == v1_temp);
+    ALWAYS_ASSERT(v2 == v2_temp);
+  }
+
+  TestCursor();
+
+  cout << "encoder test passed" << endl;
+}
+
+}
+
+class main_thread : public ndb_thread {
+public:
+  main_thread(int argc, char **argv)
+    : ndb_thread(false, string("main")),
+      argc(argc), argv(argv), ret(0)
+  {}
+
+  virtual void
+  run()
+  {
+#ifndef CHECK_INVARIANTS
+    cerr << "WARNING: tests are running without invariant checking" << endl;
+#endif
+    cerr << "PID: " << getpid() << endl;
+
+    CircbufTest();
+
+    // initialize the numa allocator subsystem with the number of CPUs running
+    // + reasonable size per core
+    ::allocator::Initialize(coreid::num_cpus_online(), size_t(128 * (1<<20)));
+#ifdef PROTO2_CAN_DISABLE_GC
+    transaction_proto2_static::InitGC();
+#endif
+    //varkeytest::Test();
+    //pxqueuetest::Test();
+    //CounterTest();
+    //UtilTest();
+    //varint::Test();
+    //small_vector_ns::Test();
+    //small_map_ns::Test();
+    //recordtest::Test();
+    //rcu::Test();
+    extern void TestConcurrentBtreeFast();
+    extern void TestConcurrentBtreeSlow();
+    // either tests Masstree or Silotree, depending on NDB_MASSTREE
+    TestConcurrentBtreeFast();
+    TestConcurrentBtreeSlow();
+    txn_btree_test();
+    ret = 0;
+  }
+
+  inline int
+  retval() const
+  {
+    return ret;
+  }
+private:
+  const int argc;
+  char **const argv;
+  volatile int ret;
+};
+
+int
+main(int argc, char **argv)
+{
+  main_thread t(argc, argv);
+  t.start();
+  t.join();
+  return t.retval();
+}
diff --git a/silo/third-party/lz4/LZ4 Streaming Format.odt b/silo/third-party/lz4/LZ4 Streaming Format.odt
new file mode 100644 (file)
index 0000000..0d8e988
Binary files /dev/null and b/silo/third-party/lz4/LZ4 Streaming Format.odt differ
diff --git a/silo/third-party/lz4/Makefile b/silo/third-party/lz4/Makefile
new file mode 100644 (file)
index 0000000..9214589
--- /dev/null
@@ -0,0 +1,40 @@
+CC=gcc
+CFLAGS=-I. -std=c99 -Wall -W -Wundef -Wno-implicit-function-declaration
+
+OS := $(shell uname)
+ifeq ($(OS),Linux)
+EXT =
+else
+EXT =.exe
+endif
+
+default: lz4c
+
+all: lz4c lz4cs lz4c32 fuzzer fullbench
+
+lz4c: lz4.c lz4hc.c bench.c xxhash.c lz4c.c
+       $(CC)      -O3 $(CFLAGS) $^ -o $@$(EXT)
+
+lz4cs: lz4.c lz4hc.c bench.c xxhash.c lz4c.c
+       $(CC)      -Os $(CFLAGS) $^ -o $@$(EXT)
+
+lz4c32: lz4.c lz4hc.c bench.c xxhash.c lz4c.c
+       $(CC) -m32 -O3 $(CFLAGS) $^ -o $@$(EXT)
+
+fuzzer : lz4.c lz4hc.c fuzzer.c
+       $(CC)      -O3 $(CFLAGS) $^ -o $@$(EXT)
+       
+fullbench : lz4.c lz4hc.c xxhash.c fullbench.c
+       $(CC)      -O3 $(CFLAGS) $^ -o $@$(EXT)
+
+.PHONY: library
+library: liblz4.so
+
+%.o: %.c
+       $(CC) -fPIC -O3 $(CFLAGS) -c $< -o $@
+
+liblz4.so: lz4.o xxhash.o
+       $(CC) -shared -Wl,-soname,liblz4.so -o liblz4.so lz4.o xxhash.o
+
+clean:
+       rm -f core *.o *.so lz4c$(EXT) lz4cs$(EXT) lz4c32$(EXT) fuzzer$(EXT) fullbench$(EXT)
diff --git a/silo/third-party/lz4/bench.c b/silo/third-party/lz4/bench.c
new file mode 100644 (file)
index 0000000..f605249
--- /dev/null
@@ -0,0 +1,434 @@
+/*\r
+    bench.c - Demo program to benchmark open-source compression algorithm\r
+    Copyright (C) Yann Collet 2012-2013\r
+    GPL v2 License\r
+\r
+    This program is free software; you can redistribute it and/or modify\r
+    it under the terms of the GNU General Public License as published by\r
+    the Free Software Foundation; either version 2 of the License, or\r
+    (at your option) any later version.\r
+\r
+    This program is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+    GNU General Public License for more details.\r
+\r
+    You should have received a copy of the GNU General Public License along\r
+    with this program; if not, write to the Free Software Foundation, Inc.,\r
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+    You can contact the author at :\r
+    - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+    - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+\r
+//**************************************\r
+// Compiler Options\r
+//**************************************\r
+// Disable some Visual warning messages\r
+#define _CRT_SECURE_NO_WARNINGS\r
+#define _CRT_SECURE_NO_DEPRECATE     // VS2005\r
+\r
+// Unix Large Files support (>4GB)\r
+#if (defined(__sun__) && (!defined(__LP64__)))   // Sun Solaris 32-bits requires specific definitions\r
+#  define _LARGEFILE_SOURCE \r
+#  define _FILE_OFFSET_BITS 64\r
+#elif ! defined(__LP64__)                        // No point defining Large file for 64 bit\r
+#  define _LARGEFILE64_SOURCE\r
+#endif\r
+\r
+// S_ISREG & gettimeofday() are not supported by MSVC\r
+#if defined(_MSC_VER)\r
+#  define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)\r
+#  define BMK_LEGACY_TIMER 1\r
+#endif\r
+\r
+// GCC does not support _rotl outside of Windows\r
+#if !defined(_WIN32)\r
+#  define _rotl(x,r) ((x << r) | (x >> (32 - r)))\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Includes\r
+//**************************************\r
+#include <stdlib.h>      // malloc\r
+#include <stdio.h>       // fprintf, fopen, ftello64\r
+#include <sys/types.h>   // stat64\r
+#include <sys/stat.h>    // stat64\r
+\r
+// Use ftime() if gettimeofday() is not available on your target\r
+#if defined(BMK_LEGACY_TIMER)\r
+#  include <sys/timeb.h>   // timeb, ftime\r
+#else\r
+#  include <sys/time.h>    // gettimeofday\r
+#endif\r
+\r
+#include "lz4.h"\r
+#define COMPRESSOR0 LZ4_compress\r
+#include "lz4hc.h"\r
+#define COMPRESSOR1 LZ4_compressHC\r
+#define DEFAULTCOMPRESSOR COMPRESSOR0\r
+\r
+#include "xxhash.h"\r
+\r
+\r
+//**************************************\r
+// Basic Types\r
+//**************************************\r
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99\r
+# include <stdint.h>\r
+  typedef uint8_t  BYTE;\r
+  typedef uint16_t U16;\r
+  typedef uint32_t U32;\r
+  typedef  int32_t S32;\r
+  typedef uint64_t U64;\r
+#else\r
+  typedef unsigned char       BYTE;\r
+  typedef unsigned short      U16;\r
+  typedef unsigned int        U32;\r
+  typedef   signed int        S32;\r
+  typedef unsigned long long  U64;\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Constants\r
+//**************************************\r
+#define NBLOOPS    3\r
+#define TIMELOOP   2000\r
+\r
+#define KNUTH      2654435761U\r
+#define MAX_MEM    (1984<<20)\r
+#define DEFAULT_CHUNKSIZE   (4<<20)\r
+\r
+\r
+//**************************************\r
+// Local structures\r
+//**************************************\r
+struct chunkParameters\r
+{\r
+    U32   id;\r
+    char* origBuffer;\r
+    char* compressedBuffer;\r
+    int   origSize;\r
+    int   compressedSize;\r
+};\r
+\r
+struct compressionParameters\r
+{\r
+    int (*compressionFunction)(const char*, char*, int);\r
+    int (*decompressionFunction)(const char*, char*, int);\r
+};\r
+\r
+\r
+//**************************************\r
+// MACRO\r
+//**************************************\r
+#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)\r
+\r
+\r
+\r
+//**************************************\r
+// Benchmark Parameters\r
+//**************************************\r
+static int chunkSize = DEFAULT_CHUNKSIZE;\r
+static int nbIterations = NBLOOPS;\r
+static int BMK_pause = 0;\r
+\r
+void BMK_SetBlocksize(int bsize)\r
+{\r
+    chunkSize = bsize;\r
+    DISPLAY("-Using Block Size of %i KB-\n", chunkSize>>10);\r
+}\r
+\r
+void BMK_SetNbIterations(int nbLoops)\r
+{\r
+    nbIterations = nbLoops;\r
+    DISPLAY("- %i iterations -\n", nbIterations);\r
+}\r
+\r
+void BMK_SetPause()\r
+{\r
+    BMK_pause = 1;\r
+}\r
+\r
+//*********************************************************\r
+//  Private functions\r
+//*********************************************************\r
+\r
+#if defined(BMK_LEGACY_TIMER)\r
+\r
+static int BMK_GetMilliStart()\r
+{\r
+  // Based on Legacy ftime()\r
+  // Rolls over every ~ 12.1 days (0x100000/24/60/60)\r
+  // Use GetMilliSpan to correct for rollover\r
+  struct timeb tb;\r
+  int nCount;\r
+  ftime( &tb );\r
+  nCount = (int) (tb.millitm + (tb.time & 0xfffff) * 1000);\r
+  return nCount;\r
+}\r
+\r
+#else\r
+\r
+static int BMK_GetMilliStart()\r
+{\r
+  // Based on newer gettimeofday()\r
+  // Use GetMilliSpan to correct for rollover\r
+  struct timeval tv;\r
+  int nCount;\r
+  gettimeofday(&tv, NULL);\r
+  nCount = (int) (tv.tv_usec/1000 + (tv.tv_sec & 0xfffff) * 1000);\r
+  return nCount;\r
+}\r
+\r
+#endif\r
+\r
+\r
+static int BMK_GetMilliSpan( int nTimeStart )\r
+{\r
+  int nSpan = BMK_GetMilliStart() - nTimeStart;\r
+  if ( nSpan < 0 )\r
+    nSpan += 0x100000 * 1000;\r
+  return nSpan;\r
+}\r
+\r
+\r
+static size_t BMK_findMaxMem(U64 requiredMem)\r
+{\r
+    size_t step = (64U<<20);   // 64 MB\r
+    BYTE* testmem=NULL;\r
+\r
+    requiredMem = (((requiredMem >> 25) + 1) << 26);\r
+    if (requiredMem > MAX_MEM) requiredMem = MAX_MEM;\r
+\r
+    requiredMem += 2*step;\r
+    while (!testmem)\r
+    {\r
+        requiredMem -= step;\r
+        testmem = (BYTE*) malloc ((size_t)requiredMem);\r
+    }\r
+\r
+    free (testmem);\r
+    return (size_t) (requiredMem - step);\r
+}\r
+\r
+\r
+static U64 BMK_GetFileSize(char* infilename)\r
+{\r
+    int r;\r
+#if defined(_MSC_VER)\r
+    struct _stat64 statbuf;\r
+    r = _stat64(infilename, &statbuf);\r
+#else\r
+    struct stat statbuf;\r
+    r = stat(infilename, &statbuf);\r
+#endif\r
+    if (r || !S_ISREG(statbuf.st_mode)) return 0;   // No good...\r
+    return (U64)statbuf.st_size;\r
+}\r
+\r
+\r
+//*********************************************************\r
+//  Public function\r
+//*********************************************************\r
+\r
+int BMK_benchFile(char** fileNamesTable, int nbFiles, int cLevel)\r
+{\r
+  int fileIdx=0;\r
+  FILE* fileIn;\r
+  char* infilename;\r
+  U64 largefilesize;\r
+  size_t benchedSize;\r
+  int nbChunks;\r
+  int maxCChunkSize;\r
+  size_t readSize;\r
+  char* orig_buff;\r
+  char* compressed_buff; int compressed_buff_size;\r
+  struct chunkParameters* chunkP;\r
+  U32 crcc, crcd=0;\r
+  struct compressionParameters compP;\r
+\r
+  U64 totals = 0;\r
+  U64 totalz = 0;\r
+  double totalc = 0.;\r
+  double totald = 0.;\r
+\r
+\r
+  // Init\r
+  switch (cLevel)\r
+  {\r
+#ifdef COMPRESSOR0\r
+  case 0 : compP.compressionFunction = COMPRESSOR0; break;\r
+#endif\r
+#ifdef COMPRESSOR1\r
+  case 1 : compP.compressionFunction = COMPRESSOR1; break;\r
+#endif\r
+  default : compP.compressionFunction = DEFAULTCOMPRESSOR;\r
+  }\r
+  compP.decompressionFunction = LZ4_decompress_fast;\r
+\r
+  // Loop for each file\r
+  while (fileIdx<nbFiles)\r
+  {\r
+      // Check file existence\r
+      infilename = fileNamesTable[fileIdx++];\r
+      fileIn = fopen( infilename, "rb" );\r
+      if (fileIn==NULL)\r
+      {\r
+        DISPLAY( "Pb opening %s\n", infilename);\r
+        return 11;\r
+      }\r
+\r
+      // Memory allocation & restrictions\r
+      largefilesize = BMK_GetFileSize(infilename);\r
+      benchedSize = (size_t) BMK_findMaxMem(largefilesize) / 2;\r
+      if ((U64)benchedSize > largefilesize) benchedSize = (size_t)largefilesize;\r
+      if (benchedSize < largefilesize)\r
+      {\r
+          DISPLAY("Not enough memory for '%s' full size; testing %i MB only...\n", infilename, (int)(benchedSize>>20));\r
+      }\r
+\r
+      // Alloc\r
+      chunkP = (struct chunkParameters*) malloc(((benchedSize / chunkSize)+1) * sizeof(struct chunkParameters));\r
+      orig_buff = (char*)malloc((size_t )benchedSize);\r
+      nbChunks = (int) (benchedSize / chunkSize) + 1;\r
+      maxCChunkSize = LZ4_compressBound(chunkSize);\r
+      compressed_buff_size = nbChunks * maxCChunkSize;\r
+      compressed_buff = (char*)malloc((size_t )compressed_buff_size);\r
+\r
+\r
+      if(!orig_buff || !compressed_buff)\r
+      {\r
+        DISPLAY("\nError: not enough memory!\n");\r
+        free(orig_buff);\r
+        free(compressed_buff);\r
+        fclose(fileIn);\r
+        return 12;\r
+      }\r
+\r
+      // Init chunks data\r
+      {\r
+          int i;\r
+          size_t remaining = benchedSize;\r
+          char* in = orig_buff;\r
+          char* out = compressed_buff;\r
+          for (i=0; i<nbChunks; i++)\r
+          {\r
+              chunkP[i].id = i;\r
+              chunkP[i].origBuffer = in; in += chunkSize;\r
+              if ((int)remaining > chunkSize) { chunkP[i].origSize = chunkSize; remaining -= chunkSize; } else { chunkP[i].origSize = (int)remaining; remaining = 0; }\r
+              chunkP[i].compressedBuffer = out; out += maxCChunkSize;\r
+              chunkP[i].compressedSize = 0;\r
+          }\r
+      }\r
+\r
+      // Fill input buffer\r
+      DISPLAY("Loading %s...       \r", infilename);\r
+      readSize = fread(orig_buff, 1, benchedSize, fileIn);\r
+      fclose(fileIn);\r
+\r
+      if(readSize != benchedSize)\r
+      {\r
+        DISPLAY("\nError: problem reading file '%s' !!    \n", infilename);\r
+        free(orig_buff);\r
+        free(compressed_buff);\r
+        return 13;\r
+      }\r
+\r
+      // Calculating input Checksum\r
+      crcc = XXH32(orig_buff, (unsigned int)benchedSize,0);\r
+\r
+\r
+      // Bench\r
+      {\r
+        int loopNb, nb_loops, chunkNb;\r
+        size_t cSize=0;\r
+        int milliTime;\r
+        double fastestC = 100000000., fastestD = 100000000.;\r
+        double ratio=0.;\r
+\r
+        DISPLAY("\r%79s\r", "");\r
+        for (loopNb = 1; loopNb <= nbIterations; loopNb++)\r
+        {\r
+          // Compression\r
+          DISPLAY("%1i-%-14.14s : %9i ->\r", loopNb, infilename, (int)benchedSize);\r
+          { size_t i; for (i=0; i<benchedSize; i++) compressed_buff[i]=(char)i; }     // warmimg up memory\r
+\r
+          nb_loops = 0;\r
+          milliTime = BMK_GetMilliStart();\r
+          while(BMK_GetMilliStart() == milliTime);\r
+          milliTime = BMK_GetMilliStart();\r
+          while(BMK_GetMilliSpan(milliTime) < TIMELOOP)\r
+          {\r
+            for (chunkNb=0; chunkNb<nbChunks; chunkNb++)\r
+                chunkP[chunkNb].compressedSize = compP.compressionFunction(chunkP[chunkNb].origBuffer, chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origSize);\r
+            nb_loops++;\r
+          }\r
+          milliTime = BMK_GetMilliSpan(milliTime);\r
+\r
+          if ((double)milliTime < fastestC*nb_loops) fastestC = (double)milliTime/nb_loops;\r
+          cSize=0; for (chunkNb=0; chunkNb<nbChunks; chunkNb++) cSize += chunkP[chunkNb].compressedSize;\r
+          ratio = (double)cSize/(double)benchedSize*100.;\r
+\r
+          DISPLAY("%1i-%-14.14s : %9i -> %9i (%5.2f%%),%7.1f MB/s\r", loopNb, infilename, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / fastestC / 1000.);\r
+\r
+          // Decompression\r
+          { size_t i; for (i=0; i<benchedSize; i++) orig_buff[i]=0; }     // zeroing area, for CRC checking\r
+\r
+          nb_loops = 0;\r
+          milliTime = BMK_GetMilliStart();\r
+          while(BMK_GetMilliStart() == milliTime);\r
+          milliTime = BMK_GetMilliStart();\r
+          while(BMK_GetMilliSpan(milliTime) < TIMELOOP)\r
+          {\r
+            for (chunkNb=0; chunkNb<nbChunks; chunkNb++)\r
+                //chunkP[chunkNb].origSize = LZ4_decompress_safe(chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origBuffer, chunkP[chunkNb].compressedSize, chunkSize);\r
+                chunkP[chunkNb].compressedSize = LZ4_decompress_fast(chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origBuffer, chunkP[chunkNb].origSize);\r
+                //chunkP[chunkNb].compressedSize = LZ4_decompress_fast_withPrefix64k(chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origBuffer, chunkP[chunkNb].origSize);\r
+                //chunkP[chunkNb].origSize = LZ4_decompress_safe_withPrefix64k(chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origBuffer, chunkP[chunkNb].compressedSize, chunkSize);\r
+                //chunkP[chunkNb].origSize = LZ4_decompress_safe_partial(chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origBuffer, chunkP[chunkNb].compressedSize, chunkSize-5, chunkSize);\r
+                //chunkP[chunkNb].compressedSize = LZ4_uncompress(chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origBuffer, chunkP[chunkNb].origSize);\r
+                //chunkP[chunkNb].origSize = LZ4_uncompress_unknownOutputSize(chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origBuffer, chunkP[chunkNb].compressedSize, chunkSize);\r
+            nb_loops++;\r
+          }\r
+          milliTime = BMK_GetMilliSpan(milliTime);\r
+\r
+          if ((double)milliTime < fastestD*nb_loops) fastestD = (double)milliTime/nb_loops;\r
+          DISPLAY("%1i-%-14.14s : %9i -> %9i (%5.2f%%),%7.1f MB/s ,%7.1f MB/s\r", loopNb, infilename, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / fastestC / 1000., (double)benchedSize / fastestD / 1000.);\r
+\r
+          // CRC Checking\r
+          crcd = XXH32(orig_buff, (unsigned int)benchedSize,0);\r
+          if (crcc!=crcd) { DISPLAY("\n!!! WARNING !!! %14s : Invalid Checksum : %x != %x\n", infilename, (unsigned)crcc, (unsigned)crcd); break; }\r
+        }\r
+\r
+        if (crcc==crcd)\r
+        {\r
+            if (ratio<100.)\r
+                DISPLAY("%-16.16s : %9i -> %9i (%5.2f%%),%7.1f MB/s ,%7.1f MB/s\n", infilename, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / fastestC / 1000., (double)benchedSize / fastestD / 1000.);\r
+            else\r
+                DISPLAY("%-16.16s : %9i -> %9i (%5.1f%%),%7.1f MB/s ,%7.1f MB/s \n", infilename, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / fastestC / 1000., (double)benchedSize / fastestD / 1000.);\r
+        }\r
+        totals += benchedSize;\r
+        totalz += cSize;\r
+        totalc += fastestC;\r
+        totald += fastestD;\r
+      }\r
+\r
+      free(orig_buff);\r
+      free(compressed_buff);\r
+      free(chunkP);\r
+  }\r
+\r
+  if (nbFiles > 1)\r
+        DISPLAY("%-16.16s :%10llu ->%10llu (%5.2f%%), %6.1f MB/s , %6.1f MB/s\n", "  TOTAL", (long long unsigned int)totals, (long long unsigned int)totalz, (double)totalz/(double)totals*100., (double)totals/totalc/1000., (double)totals/totald/1000.);\r
+\r
+  if (BMK_pause) { DISPLAY("press enter...\n"); getchar(); }\r
+\r
+  return 0;\r
+}\r
+\r
+\r
+\r
diff --git a/silo/third-party/lz4/bench.h b/silo/third-party/lz4/bench.h
new file mode 100644 (file)
index 0000000..9d5e4f5
--- /dev/null
@@ -0,0 +1,41 @@
+/*\r
+    bench.h - Demo program to benchmark open-source compression algorithm\r
+    Copyright (C) Yann Collet 2012-2013\r
+\r
+    This program is free software; you can redistribute it and/or modify\r
+    it under the terms of the GNU General Public License as published by\r
+    the Free Software Foundation; either version 2 of the License, or\r
+    (at your option) any later version.\r
+\r
+    This program is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+    GNU General Public License for more details.\r
+\r
+    You should have received a copy of the GNU General Public License along\r
+    with this program; if not, write to the Free Software Foundation, Inc.,\r
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+       You can contact the author at :\r
+       - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+       - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+#pragma once\r
+\r
+#if defined (__cplusplus)\r
+extern "C" {\r
+#endif\r
+\r
+\r
+int BMK_benchFile(char** fileNamesTable, int nbFiles, int cLevel);\r
+\r
+// Parameters\r
+void BMK_SetBlocksize(int bsize);\r
+void BMK_SetNbIterations(int nbLoops);\r
+void BMK_SetPause();\r
+\r
+\r
+\r
+#if defined (__cplusplus)\r
+}\r
+#endif\r
diff --git a/silo/third-party/lz4/cmake/CMakeLists.txt b/silo/third-party/lz4/cmake/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1afe650
--- /dev/null
@@ -0,0 +1,106 @@
+PROJECT(LZ4)\r
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ASN.1 Compiler")\r
+set(CPACK_PACKAGE_VERSION_MAJOR 0)\r
+set(CPACK_PACKAGE_VERSION_MINOR 0)\r
+set(CPACK_PACKAGE_VERSION_PATCH r52)\r
+#set(CPACK_RESOURCE_FILE_LICENSE  ${CMAKE_CURRENT_BINARY_DIR}/COPYING_LGPL)\r
+set(VERSION_STRING     " \"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}\" ")\r
+include(CPack)\r
+\r
+\r
+cmake_minimum_required (VERSION 2.6)\r
+INCLUDE (CheckTypeSize)\r
+check_type_size("void *" SIZEOF_VOID_P)\r
+IF( ${SIZEOF_VOID_P} STREQUAL  "8" )\r
+    set (CMAKE_SYSTEM_PROCESSOR "64bit")\r
+    MESSAGE( STATUS "64 bit architecture detected size of void * is " ${SIZEOF_VOID_P})\r
+ENDIF()\r
+\r
+set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries")\r
+\r
+if (BUILD_SHARED_LIBS)\r
+    message(STATUS "Enable shared library building")\r
+else(BUILD_SHARED_LIBS)\r
+    message(STATUS "Disable shared library building")\r
+endif(BUILD_SHARED_LIBS)\r
+\r
+if(UNIX AND BUILD_SHARED_LIBS)\r
+    if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")\r
+        add_definitions(-fPIC)\r
+    endif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")\r
+endif()\r
+\r
+set(SRC_DIR ../)\r
+set(LZ4_SRCS_LIB ${SRC_DIR}lz4.c ${SRC_DIR}lz4hc.c ${SRC_DIR}lz4.h )\r
+set(LZ4_SRCS ${SRC_DIR}xxhash.c ${SRC_DIR}bench.c ${SRC_DIR}lz4c.c)\r
+\r
+if(NOT BUILD_SHARED_LIBS)\r
+    set(LZ4_SRCS ${LZ4_SRCS} ${LZ4_SRCS_LIB})\r
+endif()\r
+\r
+if (CMAKE_SYSTEM_PROCESSOR STREQUAL "64bit")\r
+    message(STATUS "Build 64bit executable binary")\r
+    add_executable(lz4c64 ${LZ4_SRCS})\r
+    install(TARGETS lz4c64 RUNTIME DESTINATION "./")\r
+    if(NOT BUILD_SHARED_LIBS)\r
+        message(STATUS "Build 32bit executable binary")\r
+        add_executable(lz4c32 ${LZ4_SRCS})\r
+        install(TARGETS lz4c32 RUNTIME DESTINATION "./")\r
+\r
+        SET_TARGET_PROPERTIES(lz4c32 PROPERTIES\r
+            COMPILE_FLAGS PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")\r
+    endif()\r
+else()\r
+    message(STATUS "Build 32bit executable binary")\r
+    add_executable(lz4c32 ${LZ4_SRCS})\r
+    install(TARGETS lz4c32 RUNTIME DESTINATION "./")\r
+endif()\r
+\r
+if(BUILD_SHARED_LIBS)\r
+    set(LZ4_SOVERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}")\r
+    if (CMAKE_SYSTEM_PROCESSOR STREQUAL "64bit")\r
+        target_link_libraries(lz4c64 liblz4)\r
+    else()\r
+        target_link_libraries(lz4c32 liblz4)\r
+    endif()\r
+       \r
+    # for static library\r
+    add_library(liblz4_static STATIC ${LZ4_SRCS_LIB})\r
+    set_target_properties(liblz4_static PROPERTIES OUTPUT_NAME lz4)\r
+\r
+    install(TARGETS liblz4_static\r
+        LIBRARY DESTINATION lib\r
+        ARCHIVE DESTINATION lib\r
+    )\r
+\r
+    # for shared library o\r
+    add_library(liblz4 ${LZ4_SRCS_LIB})\r
+    set_target_properties(liblz4 PROPERTIES\r
+            OUTPUT_NAME lz4\r
+            SOVERSION ${LZ4_SOVERSION})\r
+\r
+    install(TARGETS liblz4\r
+        LIBRARY DESTINATION lib\r
+        ARCHIVE DESTINATION lib\r
+    )\r
+    \r
+    install(FILES\r
+        ${SRC_DIR}/lz4.h\r
+        ${SRC_DIR}/lz4hc.h\r
+        DESTINATION include)\r
+            \r
+endif(BUILD_SHARED_LIBS)\r
+\r
+\r
+#warnings\r
+\r
+ADD_DEFINITIONS("-Wall")\r
+ADD_DEFINITIONS("-W")\r
+ADD_DEFINITIONS("-Wundef")\r
+ADD_DEFINITIONS("-Wcast-align")\r
+ADD_DEFINITIONS("-Wno-implicit-function-declaration")\r
+ADD_DEFINITIONS("-Os -march=native -std=c99")\r
+INCLUDE_DIRECTORIES (${SRC_DIR})\r
+\r
+\r
+\r
diff --git a/silo/third-party/lz4/cmake/pack/CMakeLists.txt b/silo/third-party/lz4/cmake/pack/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8e59824
--- /dev/null
@@ -0,0 +1,82 @@
+cmake_minimum_required (VERSION 2.8)\r
+PROJECT(LZ4)\r
+\r
+############################## CPACK\r
+set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../)\r
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "LZ4 Packer")\r
+set(CPACK_PACKAGE_VERSION_MAJOR 0)\r
+set(CPACK_PACKAGE_VERSION_MINOR 0)\r
+set(CPACK_PACKAGE_VERSION_PATCH r89)\r
+#set(CPACK_RESOURCE_FILE_LICENSE  ${CMAKE_CURRENT_BINARY_DIR}/COPYING_LGPL)\r
+##############################\r
+FIND_PACKAGE(Subversion)\r
+IF(SUBVERSION_FOUND)\r
+       Subversion_WC_INFO(${SRC_DIR}  revision)\r
+       set(revision_MY_WC_STATUS "LZ4 has revision ${revision_WC_REVISION} at ${revision_WC_LAST_CHANGED_DATE}")\r
+       message(STATUS ${revision_MY_WC_STATUS})\r
+       if(NOT ${revision_WC_REVISION})\r
+       else(NOT ${revision_WC_REVISION})\r
+               set(CPACK_PACKAGE_VERSION_PATCH "r${revision_WC_REVISION}")\r
+       endif(NOT ${revision_WC_REVISION})\r
+ELSE(SUBVERSION_FOUND)\r
+       message(WARNING "NO Subversion FOUND!!!")\r
+ENDIF(SUBVERSION_FOUND)\r
+set(VERSION_STRING     " \"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}\" ")\r
+##############################\r
+INCLUDE (CheckTypeSize)\r
+check_type_size("void *" SIZEOF_VOID_P)\r
+IF( ${SIZEOF_VOID_P} STREQUAL  "8" )\r
+    set (CMAKE_SYSTEM_PROCESSOR "64bit")\r
+    MESSAGE( STATUS "64 bit architecture detected size of void * is " ${SIZEOF_VOID_P})\r
+ENDIF()\r
+###############################     warnings\r
+\r
+ADD_DEFINITIONS("-Wall")\r
+ADD_DEFINITIONS("-W")\r
+ADD_DEFINITIONS("-Wundef")\r
+ADD_DEFINITIONS("-Wcast-align")\r
+ADD_DEFINITIONS("-Wno-implicit-function-declaration")\r
+ADD_DEFINITIONS("-O3 -march=native -std=c99")\r
+INCLUDE_DIRECTORIES (${SRC_DIR})\r
+\r
+\r
+\r
+set(LZ4_SRCS_LIB ${SRC_DIR}lz4.c ${SRC_DIR}lz4hc.c ${SRC_DIR}lz4.h ${SRC_DIR}lz4_format_description.txt)\r
+set(LZ4_SRCS ${LZ4_SRCS_LIB} ${SRC_DIR}xxhash.c ${SRC_DIR}bench.c ${SRC_DIR}lz4c.c )\r
+set(FUZZER_SRCS ${SRC_DIR}lz4.c ${SRC_DIR}lz4hc.c ${SRC_DIR}lz4.h ${SRC_DIR}fuzzer.c)\r
+\r
+# EXECUTABLES FOR 32 Bit and 64 versions\r
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL "64bit")\r
+       add_executable(lz4c32 ${LZ4_SRCS})\r
+       install(TARGETS lz4c32 RUNTIME DESTINATION "./")\r
+SET_TARGET_PROPERTIES(lz4c32 PROPERTIES\r
+       COMPILE_FLAGS PROPERTIES COMPILE_FLAGS "-m32 -Os" LINK_FLAGS "-m32")\r
+endif()\r
+\r
+add_executable(lz4c ${LZ4_SRCS})\r
+install(TARGETS lz4c RUNTIME DESTINATION "./")\r
+\r
+add_executable(fuzzer ${FUZZER_SRCS})\r
+install(TARGETS fuzzer RUNTIME DESTINATION "./")\r
+\r
+#target_link_libraries(lz4 ${LZ4_SRCS_LIB})\r
+####################### CPACK PACKAGING ###################\r
+install(FILES ${SRC_DIR}lz4_format_description.txt DESTINATION "./")\r
+\r
+set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME})\r
+set(CPACK_RESOURCE_FILE_LICENSE  "${CMAKE_CURRENT_LIST_DIR}/release_COPYING.txt")\r
+set(CPACK_PACKAGE_VENDOR "Yann Collet")\r
+set(CPACK_WWW_SITE "http://fastcompression.blogspot.com/p/lz4.html")\r
+set(CPACK_NSIS_URL_INFO_ABOUT "${CPACK_WWW_SITE}")\r
+set(CPACK_NSIS_HELP_LINK "${CPACK_WWW_SITE}")\r
+\r
+set(CPACK_NSIS_DISPLAY_NAME ${CPACK_PACKAGE_NAME})\r
+set(CPACK_NSIS_COMPRESSOR "/SOLID lzma \r\n SetCompressorDictSize 32")\r
+\r
+set(CPACK_NSIS_MENU_LINKS\r
+       "${CPACK_WWW_SITE}" "${CPACK_PACKAGE_NAME} homepage"\r
+       "/lz4_format_description.txt "  "lz4 format description"\r
+       "/"     "LZ4 directory "\r
+)\r
+\r
+include (CPack)\r
diff --git a/silo/third-party/lz4/cmake/pack/release_COPYING.txt b/silo/third-party/lz4/cmake/pack/release_COPYING.txt
new file mode 100644 (file)
index 0000000..32cec0d
--- /dev/null
@@ -0,0 +1,21 @@
+\r
+    lz4demo and fuzzer is an open-source demo compression algorithm LZ4 programs\r
+    Copyright (C) Yann Collet 2012\r
+\r
+    This program is free software; you can redistribute it and/or modify\r
+    it under the terms of the GNU General Public License as published by\r
+    the Free Software Foundation; either version 2 of the License, or\r
+    (at your option) any later version.\r
+\r
+    This program is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+    GNU General Public License for more details.\r
+\r
+    You should have received a copy of the GNU General Public License along\r
+    with this program; if not, write to the Free Software Foundation, Inc.,\r
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+       You can contact the author at :\r
+       - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+       - LZ4 source repository : http://code.google.com/p/lz4/\r
diff --git a/silo/third-party/lz4/fullbench.c b/silo/third-party/lz4/fullbench.c
new file mode 100644 (file)
index 0000000..e64664d
--- /dev/null
@@ -0,0 +1,638 @@
+/*\r
+    bench.c - Demo program to benchmark open-source compression algorithm\r
+    Copyright (C) Yann Collet 2012-2013\r
+    GPL v2 License\r
+\r
+    This program is free software; you can redistribute it and/or modify\r
+    it under the terms of the GNU General Public License as published by\r
+    the Free Software Foundation; either version 2 of the License, or\r
+    (at your option) any later version.\r
+\r
+    This program is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+    GNU General Public License for more details.\r
+\r
+    You should have received a copy of the GNU General Public License along\r
+    with this program; if not, write to the Free Software Foundation, Inc.,\r
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+    You can contact the author at :\r
+    - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+    - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+\r
+//**************************************\r
+// Compiler Options\r
+//**************************************\r
+// Disable some Visual warning messages\r
+#define _CRT_SECURE_NO_WARNINGS\r
+#define _CRT_SECURE_NO_DEPRECATE     // VS2005\r
+\r
+// Unix Large Files support (>4GB)\r
+#if (defined(__sun__) && (!defined(__LP64__)))   // Sun Solaris 32-bits requires specific definitions\r
+#  define _LARGEFILE_SOURCE \r
+#  define _FILE_OFFSET_BITS 64\r
+#elif ! defined(__LP64__)                        // No point defining Large file for 64 bit\r
+#  define _LARGEFILE64_SOURCE\r
+#endif\r
+\r
+// S_ISREG & gettimeofday() are not supported by MSVC\r
+#if defined(_MSC_VER)\r
+#  define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)\r
+#  define BMK_LEGACY_TIMER 1\r
+#endif\r
+\r
+// GCC does not support _rotl outside of Windows\r
+#if !defined(_WIN32)\r
+#  define _rotl(x,r) ((x << r) | (x >> (32 - r)))\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Includes\r
+//**************************************\r
+#include <stdlib.h>      // malloc\r
+#include <stdio.h>       // fprintf, fopen, ftello64\r
+#include <sys/types.h>   // stat64\r
+#include <sys/stat.h>    // stat64\r
+\r
+// Use ftime() if gettimeofday() is not available on your target\r
+#if defined(BMK_LEGACY_TIMER)\r
+#  include <sys/timeb.h>   // timeb, ftime\r
+#else\r
+#  include <sys/time.h>    // gettimeofday\r
+#endif\r
+\r
+#include "lz4.h"\r
+#define COMPRESSOR0 LZ4_compress\r
+#include "lz4hc.h"\r
+#define COMPRESSOR1 LZ4_compressHC\r
+#define DEFAULTCOMPRESSOR COMPRESSOR0\r
+\r
+#include "xxhash.h"\r
+\r
+\r
+//**************************************\r
+// Basic Types\r
+//**************************************\r
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99\r
+# include <stdint.h>\r
+  typedef uint8_t  BYTE;\r
+  typedef uint16_t U16;\r
+  typedef uint32_t U32;\r
+  typedef  int32_t S32;\r
+  typedef uint64_t U64;\r
+#else\r
+  typedef unsigned char       BYTE;\r
+  typedef unsigned short      U16;\r
+  typedef unsigned int        U32;\r
+  typedef   signed int        S32;\r
+  typedef unsigned long long  U64;\r
+#endif\r
+\r
+\r
+//****************************\r
+// Constants\r
+//****************************\r
+#define COMPRESSOR_NAME "Full LZ4 speed analyzer"\r
+#define COMPRESSOR_VERSION ""\r
+#define COMPILED __DATE__\r
+#define AUTHOR "Yann Collet"\r
+#define WELCOME_MESSAGE "*** %s %s, by %s (%s) ***\n", COMPRESSOR_NAME, COMPRESSOR_VERSION, AUTHOR, COMPILED\r
+\r
+#define NBLOOPS    6\r
+#define TIMELOOP   2500\r
+\r
+#define KNUTH      2654435761U\r
+#define MAX_MEM    (1984<<20)\r
+#define DEFAULT_CHUNKSIZE   (4<<20)\r
+\r
+\r
+//**************************************\r
+// Local structures\r
+//**************************************\r
+struct chunkParameters\r
+{\r
+    U32   id;\r
+    char* origBuffer;\r
+    char* compressedBuffer;\r
+    int   origSize;\r
+    int   compressedSize;\r
+};\r
+\r
+\r
+//**************************************\r
+// MACRO\r
+//**************************************\r
+#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)\r
+\r
+\r
+\r
+//**************************************\r
+// Benchmark Parameters\r
+//**************************************\r
+static int chunkSize = DEFAULT_CHUNKSIZE;\r
+static int nbIterations = NBLOOPS;\r
+static int BMK_pause = 0;\r
+static int compressionTest = 1;\r
+static int decompressionTest = 1;\r
+\r
+void BMK_SetBlocksize(int bsize)\r
+{\r
+    chunkSize = bsize;\r
+    DISPLAY("-Using Block Size of %i KB-\n", chunkSize>>10);\r
+}\r
+\r
+void BMK_SetNbIterations(int nbLoops)\r
+{\r
+    nbIterations = nbLoops;\r
+    DISPLAY("- %i iterations -\n", nbIterations);\r
+}\r
+\r
+void BMK_SetPause()\r
+{\r
+    BMK_pause = 1;\r
+}\r
+\r
+//*********************************************************\r
+//  Private functions\r
+//*********************************************************\r
+\r
+#if defined(BMK_LEGACY_TIMER)\r
+\r
+static int BMK_GetMilliStart()\r
+{\r
+  // Based on Legacy ftime()\r
+  // Rolls over every ~ 12.1 days (0x100000/24/60/60)\r
+  // Use GetMilliSpan to correct for rollover\r
+  struct timeb tb;\r
+  int nCount;\r
+  ftime( &tb );\r
+  nCount = (int) (tb.millitm + (tb.time & 0xfffff) * 1000);\r
+  return nCount;\r
+}\r
+\r
+#else\r
+\r
+static int BMK_GetMilliStart()\r
+{\r
+  // Based on newer gettimeofday()\r
+  // Use GetMilliSpan to correct for rollover\r
+  struct timeval tv;\r
+  int nCount;\r
+  gettimeofday(&tv, NULL);\r
+  nCount = (int) (tv.tv_usec/1000 + (tv.tv_sec & 0xfffff) * 1000);\r
+  return nCount;\r
+}\r
+\r
+#endif\r
+\r
+\r
+static int BMK_GetMilliSpan( int nTimeStart )\r
+{\r
+  int nSpan = BMK_GetMilliStart() - nTimeStart;\r
+  if ( nSpan < 0 )\r
+    nSpan += 0x100000 * 1000;\r
+  return nSpan;\r
+}\r
+\r
+\r
+static size_t BMK_findMaxMem(U64 requiredMem)\r
+{\r
+    size_t step = (64U<<20);   // 64 MB\r
+    BYTE* testmem=NULL;\r
+\r
+    requiredMem = (((requiredMem >> 25) + 1) << 26);\r
+    if (requiredMem > MAX_MEM) requiredMem = MAX_MEM;\r
+\r
+    requiredMem += 2*step;\r
+    while (!testmem)\r
+    {\r
+        requiredMem -= step;\r
+        testmem = (BYTE*) malloc ((size_t)requiredMem);\r
+    }\r
+\r
+    free (testmem);\r
+    return (size_t) (requiredMem - step);\r
+}\r
+\r
+\r
+static U64 BMK_GetFileSize(char* infilename)\r
+{\r
+    int r;\r
+#if defined(_MSC_VER)\r
+    struct _stat64 statbuf;\r
+    r = _stat64(infilename, &statbuf);\r
+#else\r
+    struct stat statbuf;\r
+    r = stat(infilename, &statbuf);\r
+#endif\r
+    if (r || !S_ISREG(statbuf.st_mode)) return 0;   // No good...\r
+    return (U64)statbuf.st_size;\r
+}\r
+\r
+\r
+//*********************************************************\r
+//  Public function\r
+//*********************************************************\r
+\r
+static inline int local_LZ4_compress_limitedOutput(const char* in, char* out, int inSize)\r
+{\r
+    return LZ4_compress_limitedOutput(in, out, inSize, LZ4_compressBound(inSize));\r
+}\r
+\r
+static inline int local_LZ4_compressHC_limitedOutput(const char* in, char* out, int inSize)\r
+{\r
+    return LZ4_compressHC_limitedOutput(in, out, inSize, LZ4_compressBound(inSize));\r
+}\r
+\r
+static inline int local_LZ4_decompress_fast(const char* in, char* out, int inSize, int outSize)\r
+{\r
+    (void)inSize;\r
+    LZ4_decompress_fast(in, out, outSize);\r
+    return outSize;\r
+}\r
+\r
+static inline int local_LZ4_decompress_fast_withPrefix64k(const char* in, char* out, int inSize, int outSize)\r
+{\r
+    (void)inSize;\r
+    LZ4_decompress_fast_withPrefix64k(in, out, outSize);\r
+    return outSize;\r
+}\r
+\r
+static inline int local_LZ4_decompress_safe_partial(const char* in, char* out, int inSize, int outSize)\r
+{\r
+    return LZ4_decompress_safe_partial(in, out, inSize, outSize - 5, outSize);\r
+}\r
+\r
+int fullSpeedBench(char** fileNamesTable, int nbFiles)\r
+{\r
+  int fileIdx=0;\r
+  FILE* fileIn;\r
+  char* infilename;\r
+  U64 largefilesize;\r
+  size_t benchedSize;\r
+  int nbChunks;\r
+  int maxCChunkSize;\r
+  size_t readSize;\r
+  char* orig_buff;\r
+  char* compressed_buff; int compressed_buff_size;\r
+  struct chunkParameters* chunkP;\r
+  U32 crcc, crcd=0;\r
+# define NB_COMPRESSION_ALGORITHMS 4\r
+  static char* compressionNames[] = { "LZ4_compress", "LZ4_compress_limitedOutput", "LZ4_compressHC", "LZ4_compressHC_limitedOutput" };\r
+  double totalCTime[NB_COMPRESSION_ALGORITHMS] = {0};\r
+  double totalCSize[NB_COMPRESSION_ALGORITHMS] = {0};\r
+# define NB_DECOMPRESSION_ALGORITHMS 5\r
+  static char* decompressionNames[] = { "LZ4_decompress_fast", "LZ4_decompress_fast_withPrefix64k", "LZ4_decompress_safe", "LZ4_decompress_safe_withPrefix64k", "LZ4_decompress_safe_partial" };\r
+  double totalDTime[NB_DECOMPRESSION_ALGORITHMS] = {0};\r
+\r
+  U64 totals = 0;\r
+\r
+\r
+  // Loop for each file\r
+  while (fileIdx<nbFiles)\r
+  {\r
+      // Check file existence\r
+      infilename = fileNamesTable[fileIdx++];\r
+      fileIn = fopen( infilename, "rb" );\r
+      if (fileIn==NULL)\r
+      {\r
+        DISPLAY( "Pb opening %s\n", infilename);\r
+        return 11;\r
+      }\r
+\r
+      // Memory allocation & restrictions\r
+      largefilesize = BMK_GetFileSize(infilename);\r
+      benchedSize = (size_t) BMK_findMaxMem(largefilesize) / 2;\r
+      if ((U64)benchedSize > largefilesize) benchedSize = (size_t)largefilesize;\r
+      if (benchedSize < largefilesize)\r
+      {\r
+          DISPLAY("Not enough memory for '%s' full size; testing %i MB only...\n", infilename, (int)(benchedSize>>20));\r
+      }\r
+\r
+      // Alloc\r
+      chunkP = (struct chunkParameters*) malloc(((benchedSize / chunkSize)+1) * sizeof(struct chunkParameters));\r
+      orig_buff = (char*) malloc((size_t)benchedSize);\r
+      nbChunks = (int) (benchedSize / chunkSize) + 1;\r
+      maxCChunkSize = LZ4_compressBound(chunkSize);\r
+      compressed_buff_size = nbChunks * maxCChunkSize;\r
+      compressed_buff = (char*)malloc((size_t)compressed_buff_size);\r
+\r
+\r
+      if(!orig_buff || !compressed_buff)\r
+      {\r
+        DISPLAY("\nError: not enough memory!\n");\r
+        free(orig_buff);\r
+        free(compressed_buff);\r
+        fclose(fileIn);\r
+        return 12;\r
+      }\r
+\r
+      // Init chunks data\r
+      {\r
+          int i;\r
+          size_t remaining = benchedSize;\r
+          char* in = orig_buff;\r
+          char* out = compressed_buff;\r
+          for (i=0; i<nbChunks; i++)\r
+          {\r
+              chunkP[i].id = i;\r
+              chunkP[i].origBuffer = in; in += chunkSize;\r
+              if ((int)remaining > chunkSize) { chunkP[i].origSize = chunkSize; remaining -= chunkSize; } else { chunkP[i].origSize = (int)remaining; remaining = 0; }\r
+              chunkP[i].compressedBuffer = out; out += maxCChunkSize;\r
+              chunkP[i].compressedSize = 0;\r
+          }\r
+      }\r
+\r
+      // Fill input buffer\r
+      DISPLAY("Loading %s...       \r", infilename);\r
+      readSize = fread(orig_buff, 1, benchedSize, fileIn);\r
+      fclose(fileIn);\r
+\r
+      if(readSize != benchedSize)\r
+      {\r
+        DISPLAY("\nError: problem reading file '%s' !!    \n", infilename);\r
+        free(orig_buff);\r
+        free(compressed_buff);\r
+        return 13;\r
+      }\r
+\r
+      // Calculating input Checksum\r
+      crcc = XXH32(orig_buff, (unsigned int)benchedSize,0);\r
+\r
+\r
+      // Bench\r
+      {\r
+        int loopNb, nb_loops, chunkNb, cAlgNb, dAlgNb;\r
+        size_t cSize=0;\r
+        double ratio=0.;\r
+\r
+        DISPLAY("\r%79s\r", "");\r
+        DISPLAY(" %s : \n", infilename);\r
+\r
+        // Compression Algorithms\r
+        for (cAlgNb=0; (cAlgNb < NB_COMPRESSION_ALGORITHMS) && (compressionTest); cAlgNb++)\r
+        {\r
+            char* cName = compressionNames[cAlgNb];\r
+            int (*compressionFunction)(const char*, char*, int);\r
+            double bestTime = 100000000.;\r
+\r
+            switch(cAlgNb)\r
+            {\r
+            case 0: compressionFunction = LZ4_compress; break;\r
+            case 1: compressionFunction = local_LZ4_compress_limitedOutput; break;\r
+            case 2: compressionFunction = LZ4_compressHC; break;\r
+            case 3: compressionFunction = local_LZ4_compressHC_limitedOutput; break;\r
+            default : DISPLAY("ERROR ! Bad algorithm Id !! \n"); return 1;\r
+            }\r
+\r
+            for (loopNb = 1; loopNb <= nbIterations; loopNb++)\r
+            {\r
+                double averageTime;\r
+                int milliTime;\r
+\r
+                DISPLAY("%1i-%-19.19s : %9i ->\r", loopNb, cName, (int)benchedSize);\r
+                { size_t i; for (i=0; i<benchedSize; i++) compressed_buff[i]=(char)i; }     // warmimg up memory\r
+\r
+                nb_loops = 0;\r
+                milliTime = BMK_GetMilliStart();\r
+                while(BMK_GetMilliStart() == milliTime);\r
+                milliTime = BMK_GetMilliStart();\r
+                while(BMK_GetMilliSpan(milliTime) < TIMELOOP)\r
+                {\r
+                    for (chunkNb=0; chunkNb<nbChunks; chunkNb++)\r
+                    {\r
+                        chunkP[chunkNb].compressedSize = compressionFunction(chunkP[chunkNb].origBuffer, chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origSize);\r
+                        if (chunkP[chunkNb].compressedSize==0) DISPLAY("ERROR ! %s() = 0 !! \n", cName), exit(1);\r
+                    }\r
+                    nb_loops++;\r
+                }\r
+                milliTime = BMK_GetMilliSpan(milliTime);\r
+\r
+                averageTime = (double)milliTime / nb_loops;\r
+                if (averageTime < bestTime) bestTime = averageTime;\r
+                cSize=0; for (chunkNb=0; chunkNb<nbChunks; chunkNb++) cSize += chunkP[chunkNb].compressedSize;\r
+                ratio = (double)cSize/(double)benchedSize*100.;\r
+                DISPLAY("%1i-%-19.19s : %9i -> %9i (%5.2f%%),%7.1f MB/s\r", loopNb, cName, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / bestTime / 1000.);\r
+            }\r
+\r
+            if (ratio<100.)\r
+                DISPLAY("%-21.21s : %9i -> %9i (%5.2f%%),%7.1f MB/s\n", cName, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / bestTime / 1000.);\r
+            else\r
+                DISPLAY("%-21.21s : %9i -> %9i (%5.1f%%),%7.1f MB/s\n", cName, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / bestTime / 1000.);\r
+\r
+            totalCTime[cAlgNb] += bestTime;\r
+            totalCSize[cAlgNb] += cSize;\r
+        }\r
+\r
+        // Prepare layout for decompression\r
+        for (chunkNb=0; chunkNb<nbChunks; chunkNb++)\r
+        {\r
+            chunkP[chunkNb].compressedSize = LZ4_compress(chunkP[chunkNb].origBuffer, chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origSize);\r
+            if (chunkP[chunkNb].compressedSize==0) DISPLAY("ERROR ! %s() = 0 !! \n", compressionNames[0]), exit(1);\r
+        }\r
+        { size_t i; for (i=0; i<benchedSize; i++) orig_buff[i]=0; }     // zeroing source area, for CRC checking\r
+\r
+        // Decompression Algorithms\r
+        for (dAlgNb=0; (dAlgNb < NB_DECOMPRESSION_ALGORITHMS) && (decompressionTest); dAlgNb++)\r
+        {\r
+            char* dName = decompressionNames[dAlgNb];\r
+            int (*decompressionFunction)(const char*, char*, int, int);\r
+            double bestTime = 100000000.;\r
+\r
+            switch(dAlgNb)\r
+            {\r
+            case 0: decompressionFunction = local_LZ4_decompress_fast; break;\r
+            case 1: decompressionFunction = local_LZ4_decompress_fast_withPrefix64k; break;\r
+            case 2: decompressionFunction = LZ4_decompress_safe; break;\r
+            case 3: decompressionFunction = LZ4_decompress_safe_withPrefix64k; break;\r
+            case 4: decompressionFunction = local_LZ4_decompress_safe_partial; break;\r
+            default : DISPLAY("ERROR ! Bad algorithm Id !! \n"); return 1;\r
+            }\r
+\r
+            for (loopNb = 1; loopNb <= nbIterations; loopNb++)\r
+            {\r
+                double averageTime;\r
+                int milliTime;\r
+\r
+                DISPLAY("%1i-%-19.19s : %9i ->\r", loopNb, dName, (int)benchedSize);\r
+\r
+                nb_loops = 0;\r
+                milliTime = BMK_GetMilliStart();\r
+                while(BMK_GetMilliStart() == milliTime);\r
+                milliTime = BMK_GetMilliStart();\r
+                while(BMK_GetMilliSpan(milliTime) < TIMELOOP)\r
+                {\r
+                    for (chunkNb=0; chunkNb<nbChunks; chunkNb++)\r
+                    {\r
+                        int decodedSize = decompressionFunction(chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origBuffer, chunkP[chunkNb].compressedSize, chunkP[chunkNb].origSize);\r
+                        if (chunkP[chunkNb].origSize != decodedSize) DISPLAY("ERROR ! %s() == %i != %i !! \n", dName, decodedSize, chunkP[chunkNb].origSize), exit(1);\r
+                    }\r
+                    nb_loops++;\r
+                }\r
+                milliTime = BMK_GetMilliSpan(milliTime);\r
+\r
+                averageTime = (double)milliTime / nb_loops;\r
+                if (averageTime < bestTime) bestTime = averageTime;\r
+\r
+                DISPLAY("%1i-%-19.19s : %9i -> %7.1f MB/s\r", loopNb, dName, (int)benchedSize, (double)benchedSize / bestTime / 1000.);\r
+            }\r
+\r
+            // CRC Checking\r
+            crcd = XXH32(orig_buff, (int)benchedSize, 0);\r
+            if (crcc!=crcd) { DISPLAY("\n!!! WARNING !!! %14s : Invalid Checksum : %x != %x\n", infilename, (unsigned)crcc, (unsigned)crcd); exit(1); }\r
+            DISPLAY("%-21.21s : %9i -> %7.1f MB/s\n", dName, (int)benchedSize, (double)benchedSize / bestTime / 1000.);\r
+\r
+            totalDTime[dAlgNb] += bestTime;\r
+        }\r
+\r
+        totals += benchedSize;\r
+      }\r
+\r
+      free(orig_buff);\r
+      free(compressed_buff);\r
+      free(chunkP);\r
+  }\r
+\r
+  if (nbFiles > 1)\r
+  {\r
+      int AlgNb;\r
+\r
+      DISPLAY(" ** TOTAL ** : \n");\r
+      for (AlgNb = 0; (AlgNb < NB_COMPRESSION_ALGORITHMS) && (compressionTest); AlgNb ++)\r
+      {\r
+          char* cName = compressionNames[AlgNb];\r
+          DISPLAY("%-21.21s :%10llu ->%10llu (%5.2f%%), %6.1f MB/s\n", cName, (long long unsigned int)totals, (long long unsigned int)totalCSize[AlgNb], (double)totalCSize[AlgNb]/(double)totals*100., (double)totals/totalCTime[AlgNb]/1000.);\r
+      }\r
+      for (AlgNb = 0; (AlgNb < NB_DECOMPRESSION_ALGORITHMS) && (decompressionTest); AlgNb ++)\r
+      {\r
+          char* dName = decompressionNames[AlgNb];\r
+          DISPLAY("%-21.21s :%10llu -> %6.1f MB/s\n", dName, (long long unsigned int)totals, (double)totals/totalDTime[AlgNb]/1000.);\r
+      }\r
+  }\r
+\r
+  if (BMK_pause) { printf("press enter...\n"); getchar(); }\r
+\r
+  return 0;\r
+}\r
+\r
+\r
+int usage(char* exename)\r
+{\r
+    DISPLAY( "Usage :\n");\r
+    DISPLAY( "      %s [arg] file1 file2 ... fileX\n", exename);\r
+    DISPLAY( "Arguments :\n");\r
+    DISPLAY( " -c     : compression tests only\n");\r
+    DISPLAY( " -d     : decompression tests only\n");\r
+    DISPLAY( " -H     : Help (this text + advanced options)\n");\r
+    return 0;\r
+}\r
+\r
+int usage_advanced()\r
+{\r
+    DISPLAY( "\nAdvanced options :\n");\r
+    DISPLAY( " -B#    : Block size [4-7](default : 7)\n");\r
+    //DISPLAY( " -BD    : Block dependency (improve compression ratio)\n");\r
+    DISPLAY( " -i#    : iteration loops [1-9](default : 6)\n");\r
+    return 0;\r
+}\r
+\r
+int badusage(char* exename)\r
+{\r
+    DISPLAY("Wrong parameters\n");\r
+    usage(exename);\r
+    return 0;\r
+}\r
+\r
+int main(int argc, char** argv)\r
+{\r
+    int i,\r
+        filenamesStart=2;\r
+    char* exename=argv[0];\r
+    char* input_filename=0;\r
+\r
+    // Welcome message\r
+    DISPLAY( WELCOME_MESSAGE);\r
+\r
+    if (argc<2) { badusage(exename); return 1; }\r
+\r
+    for(i=1; i<argc; i++)\r
+    {\r
+        char* argument = argv[i];\r
+\r
+        if(!argument) continue;   // Protection if argument empty\r
+\r
+        // Decode command (note : aggregated commands are allowed)\r
+        if (argument[0]=='-')\r
+        {\r
+            while (argument[1]!=0)\r
+            {\r
+                argument ++;\r
+\r
+                switch(argument[0])\r
+                {\r
+                    // Select compression algorithm only\r
+                case 'c': decompressionTest = 0; break;\r
+\r
+                    // Select decompression algorithm only\r
+                case 'd': compressionTest = 0; break;\r
+\r
+                    // Display help on usage\r
+                case 'H': usage(exename); usage_advanced(); return 0;\r
+\r
+                    // Modify Block Properties\r
+                case 'B':\r
+                    while (argument[1]!=0)\r
+                    switch(argument[1])\r
+                    {\r
+                    case '4':\r
+                    case '5':\r
+                    case '6':\r
+                    case '7':\r
+                    { \r
+                        int B = argument[1] - '0'; \r
+                        int S = 1 << (8 + 2*B); \r
+                        BMK_SetBlocksize(S); \r
+                        argument++;\r
+                        break;\r
+                    }\r
+                    case 'D': argument++; break;\r
+                    default : goto _exit_blockProperties;\r
+                    }\r
+_exit_blockProperties:\r
+                    break;\r
+\r
+                    // Modify Nb Iterations\r
+                case 'i': \r
+                    if ((argument[1] >='1') && (argument[1] <='9'))\r
+                    {\r
+                        int iters = argument[1] - '0'; \r
+                        BMK_SetNbIterations(iters); \r
+                        argument++;\r
+                    }\r
+                    break;\r
+\r
+                    // Pause at the end (hidden option)\r
+                case 'p': BMK_SetPause(); break;\r
+\r
+                    // Unrecognised command\r
+                default : badusage(exename); return 1;\r
+                }\r
+            }\r
+            continue;\r
+        }\r
+\r
+        // first provided filename is input\r
+        if (!input_filename) { input_filename=argument; filenamesStart=i; continue; }\r
+\r
+    }\r
+\r
+    // No input filename ==> Error\r
+    if(!input_filename) { badusage(exename); return 1; }\r
+\r
+    return fullSpeedBench(argv+filenamesStart, argc-filenamesStart);\r
+\r
+}\r
+\r
diff --git a/silo/third-party/lz4/fuzzer.c b/silo/third-party/lz4/fuzzer.c
new file mode 100644 (file)
index 0000000..44ca885
--- /dev/null
@@ -0,0 +1,259 @@
+/*\r
+    fuzzer.c - Fuzzer test tool for LZ4\r
+    Copyright (C) Yann Collet - Andrew Mahone 2012-2013\r
+    Code started by Andrew Mahone, modified by Yann Collet\r
+    GPL v2 License\r
+\r
+    This program is free software; you can redistribute it and/or modify\r
+    it under the terms of the GNU General Public License as published by\r
+    the Free Software Foundation; either version 2 of the License, or\r
+    (at your option) any later version.\r
+\r
+    This program is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+    GNU General Public License for more details.\r
+\r
+    You should have received a copy of the GNU General Public License along\r
+    with this program; if not, write to the Free Software Foundation, Inc.,\r
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+    You can contact the author at :\r
+    - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+    - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+\r
+//**************************************\r
+// Remove Visual warning messages\r
+//**************************************\r
+#define _CRT_SECURE_NO_WARNINGS  // fgets\r
+\r
+\r
+//**************************************\r
+// Includes\r
+//**************************************\r
+#include <stdlib.h>\r
+#include <stdio.h>      // fgets, sscanf\r
+#include <sys/timeb.h>  // timeb\r
+#include "lz4.h"\r
+#include "lz4hc.h"\r
+\r
+\r
+//**************************************\r
+// Constants\r
+//**************************************\r
+#define NB_ATTEMPTS (1<<17)\r
+#define LEN ((1<<15))\r
+#define SEQ_POW 2\r
+#define NUM_SEQ (1 << SEQ_POW)\r
+#define SEQ_MSK ((NUM_SEQ) - 1)\r
+#define MOD_SEQ(x) ((((x) >> 8) & 255) == 0)\r
+#define NEW_SEQ(x) ((((x) >> 10) %10) == 0)\r
+#define PAGE_SIZE 4096\r
+#define ROUND_PAGE(x) (((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))\r
+#define PRIME1   2654435761U\r
+#define PRIME2   2246822519U\r
+#define PRIME3   3266489917U\r
+\r
+\r
+//*********************************************************\r
+//  Functions\r
+//*********************************************************\r
+static int FUZ_GetMilliStart()\r
+{\r
+   struct timeb tb;\r
+   int nCount;\r
+   ftime( &tb );\r
+   nCount = (int) (tb.millitm + (tb.time & 0xfffff) * 1000);\r
+   return nCount;\r
+}\r
+\r
+\r
+static int FUZ_GetMilliSpan( int nTimeStart )\r
+{\r
+   int nSpan = FUZ_GetMilliStart() - nTimeStart;\r
+   if ( nSpan < 0 )\r
+      nSpan += 0x100000 * 1000;\r
+   return nSpan;\r
+}\r
+\r
+\r
+unsigned int FUZ_rand(unsigned int* src)\r
+{\r
+    *src =  ((*src) * PRIME1) + PRIME2;\r
+    return *src;\r
+}\r
+\r
+\r
+int test_canary(unsigned char *buf)\r
+{\r
+    int i;\r
+    for (i = 0; i < 2048; i++)\r
+        if (buf[i] != buf[i + 2048])\r
+            return 0;\r
+    return 1;\r
+}\r
+\r
+\r
+int FUZ_SecurityTest()\r
+{\r
+  char* output;\r
+  char* input;\r
+  int i, r;\r
+\r
+  printf("Overflow test (issue 52)...");\r
+  input = (char*) malloc (20<<20);\r
+  output = (char*) malloc (20<<20);\r
+  input[0] = 0x0F;\r
+  input[1] = 0x00;\r
+  input[2] = 0x00;\r
+  for(i = 3; i < 16840000; i++)\r
+    input[i] = 0xff;\r
+  r = LZ4_decompress_fast(input, output, 20<<20);\r
+\r
+  free(input);\r
+  free(output);\r
+  printf(" Passed (return = %i < 0)\n",r);\r
+  return 0;\r
+}\r
+\r
+\r
+//int main(int argc, char *argv[]) {\r
+int main() {\r
+        unsigned long long bytes = 0;\r
+        unsigned long long cbytes = 0;\r
+        unsigned long long hcbytes = 0;\r
+        unsigned char buf[LEN];\r
+        unsigned char testOut[LEN+1];\r
+#       define FUZ_max   LZ4_COMPRESSBOUND(LEN)\r
+#       define FUZ_avail ROUND_PAGE(FUZ_max)\r
+        const int off_full = FUZ_avail - FUZ_max;\r
+        unsigned char cbuf[FUZ_avail + PAGE_SIZE];\r
+        unsigned int seed, randState, cur_seq=PRIME3, seeds[NUM_SEQ], timestamp=FUZ_GetMilliStart();\r
+        int i, j, k, ret, len, lenHC, attemptNb;\r
+        char userInput[30] = {0};\r
+#       define FUZ_CHECKTEST(cond, message) testNb++; if (cond) { printf("Test %i : %s : seed %u, cycle %u \n", testNb, message, seed, attemptNb); goto _output_error; }\r
+\r
+        printf("starting LZ4 fuzzer\n");\r
+        printf("Select an Initialisation number (default : random) : ");\r
+        fflush(stdout);\r
+        if ( fgets(userInput, sizeof userInput, stdin) )\r
+        {\r
+            if ( sscanf(userInput, "%d", &seed) == 1 ) {}\r
+            else seed = FUZ_GetMilliSpan(timestamp);\r
+        }\r
+        printf("Seed = %u\n", seed);\r
+        randState = seed;\r
+\r
+        FUZ_SecurityTest();\r
+\r
+        for (i = 0; i < 2048; i++)\r
+                cbuf[FUZ_avail + i] = cbuf[FUZ_avail + 2048 + i] = FUZ_rand(&randState) >> 16;\r
+\r
+        for (attemptNb = 0; attemptNb < NB_ATTEMPTS; attemptNb++) \r
+        {\r
+            int testNb = 0;\r
+\r
+            printf("\r%7i /%7i\r", attemptNb, NB_ATTEMPTS);\r
+            \r
+            for (j = 0; j < NUM_SEQ; j++) {\r
+                    seeds[j] = FUZ_rand(&randState) << 8;\r
+                    seeds[j] ^= (FUZ_rand(&randState) >> 8) & 65535;\r
+            }\r
+            for (j = 0; j < LEN; j++) {\r
+                    k = FUZ_rand(&randState);\r
+                    if (j == 0 || NEW_SEQ(k))\r
+                            cur_seq = seeds[(FUZ_rand(&randState) >> 16) & SEQ_MSK];\r
+                    if (MOD_SEQ(k)) {\r
+                            k = (FUZ_rand(&randState) >> 16) & SEQ_MSK;\r
+                            seeds[k] = FUZ_rand(&randState) << 8;\r
+                            seeds[k] ^= (FUZ_rand(&randState) >> 8) & 65535;\r
+                    }\r
+                    buf[j] = FUZ_rand(&cur_seq) >> 16;\r
+            }\r
+\r
+            // Test compression HC\r
+            ret = LZ4_compressHC_limitedOutput((const char*)buf, (char*)&cbuf[off_full], LEN, FUZ_max);\r
+            FUZ_CHECKTEST(ret==0, "HC compression failed despite sufficient space");\r
+            lenHC = ret;\r
+\r
+            // Test compression\r
+            ret = LZ4_compress_limitedOutput((const char*)buf, (char*)&cbuf[off_full], LEN, FUZ_max);\r
+            FUZ_CHECKTEST(ret==0, "compression failed despite sufficient space");\r
+            len = ret;\r
+\r
+            // Test decoding with output size being exactly what's necessary => must work\r
+            ret = LZ4_decompress_fast((char*)&cbuf[off_full], (char*)testOut, LEN);\r
+            FUZ_CHECKTEST(ret<0, "decompression failed despite correct space");\r
+\r
+            // Test decoding with one byte missing => must fail\r
+            ret = LZ4_decompress_fast((char*)&cbuf[off_full], (char*)testOut, LEN-1);\r
+            FUZ_CHECKTEST(ret>=0, "decompression should have failed, due to Output Size being too small");\r
+\r
+            // Test decoding with one byte too much => must fail\r
+            ret = LZ4_decompress_fast((char*)&cbuf[off_full], (char*)testOut, LEN+1);\r
+            FUZ_CHECKTEST(ret>=0, "decompression should have failed, due to Output Size being too large");\r
+\r
+            // Test decoding with enough output size => must work\r
+            ret = LZ4_decompress_safe((char*)&cbuf[off_full], (char*)testOut, len, LEN+1);\r
+            FUZ_CHECKTEST(ret<0, "decompression failed despite sufficient space");\r
+\r
+            // Test decoding with output size being exactly what's necessary => must work\r
+            ret = LZ4_decompress_safe((char*)&cbuf[off_full], (char*)testOut, len, LEN);\r
+            FUZ_CHECKTEST(ret<0, "decompression failed despite sufficient space");\r
+\r
+            // Test decoding with output size being one byte too short => must fail\r
+            ret = LZ4_decompress_safe((char*)&cbuf[off_full], (char*)testOut, len, LEN-1);\r
+            FUZ_CHECKTEST(ret>=0, "LZ4_decompress_safe should have failed, due to Output Size being one byte too short");\r
+\r
+            // Test decoding with input size being one byte too short => must fail\r
+            ret = LZ4_decompress_safe((char*)&cbuf[off_full], (char*)testOut, len-1, LEN);\r
+            FUZ_CHECKTEST(ret>=0, "LZ4_decompress_safe should have failed, due to input size being one byte too short");\r
+\r
+            // Test decoding with input size being one byte too large => must fail\r
+            ret = LZ4_decompress_safe((char*)&cbuf[off_full], (char*)testOut, len+1, LEN);\r
+            FUZ_CHECKTEST(ret>=0, "decompression should have failed, due to input size being too large");\r
+            //if (ret>=0) { printf("Test 10 : decompression should have failed, due to input size being too large : seed %u, len %d\n", seed, LEN); goto _output_error; }\r
+\r
+            // Test partial decoding with target output size being max/2 => must work\r
+            ret = LZ4_decompress_safe_partial((char*)&cbuf[off_full], (char*)testOut, len, LEN/2, LEN);\r
+            FUZ_CHECKTEST(ret<0, "partial decompression failed despite sufficient space");\r
+\r
+            // Test partial decoding with target output size being just below max => must work\r
+            ret = LZ4_decompress_safe_partial((char*)&cbuf[off_full], (char*)testOut, len, LEN-3, LEN);\r
+            FUZ_CHECKTEST(ret<0, "partial decompression failed despite sufficient space");\r
+\r
+            // Test compression with output size being exactly what's necessary (should work)\r
+            ret = LZ4_compress_limitedOutput((const char*)buf, (char*)&cbuf[FUZ_avail-len], LEN, len);\r
+            FUZ_CHECKTEST(!test_canary(&cbuf[FUZ_avail]), "compression overran output buffer");\r
+            FUZ_CHECKTEST(ret==0, "compression failed despite sufficient space");\r
+\r
+            // Test HC compression with output size being exactly what's necessary (should work)\r
+            ret = LZ4_compressHC_limitedOutput((const char*)buf, (char*)&cbuf[FUZ_avail-len], LEN, lenHC);\r
+            FUZ_CHECKTEST(ret==0, "HC compression failed despite sufficient space");\r
+\r
+            // Test compression with just one missing byte into output buffer => must fail\r
+            ret = LZ4_compress_limitedOutput((const char*)buf, (char*)&cbuf[FUZ_avail-(len-1)], LEN, len-1);\r
+            FUZ_CHECKTEST(ret, "compression overran output buffer");\r
+            FUZ_CHECKTEST(!test_canary(&cbuf[FUZ_avail]), "compression overran output buffer");\r
+\r
+            // Test HC compression with just one missing byte into output buffer => must fail\r
+            ret = LZ4_compressHC_limitedOutput((const char*)buf, (char*)&cbuf[FUZ_avail-(len-1)], LEN, lenHC-1);\r
+            FUZ_CHECKTEST(ret, "HC compression overran output buffer");\r
+\r
+            bytes += LEN;\r
+            cbytes += len;\r
+            hcbytes += lenHC;\r
+            FUZ_rand(&randState);\r
+        }\r
+\r
+        printf("all tests completed successfully \n");\r
+        printf("compression ratio: %0.3f%%\n", (double)cbytes/bytes*100);\r
+        printf("HC compression ratio: %0.3f%%\n", (double)hcbytes/bytes*100);\r
+        getchar();\r
+        return 0;\r
+\r
+_output_error:\r
+        getchar();\r
+        return 1;\r
+}\r
diff --git a/silo/third-party/lz4/liblz4.so b/silo/third-party/lz4/liblz4.so
new file mode 100755 (executable)
index 0000000..cec49d3
Binary files /dev/null and b/silo/third-party/lz4/liblz4.so differ
diff --git a/silo/third-party/lz4/lz4.c b/silo/third-party/lz4/lz4.c
new file mode 100644 (file)
index 0000000..ab35806
--- /dev/null
@@ -0,0 +1,703 @@
+/*\r
+   LZ4 - Fast LZ compression algorithm\r
+   Copyright (C) 2011-2013, Yann Collet.\r
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+   Redistribution and use in source and binary forms, with or without\r
+   modification, are permitted provided that the following conditions are\r
+   met:\r
+\r
+       * Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+       * Redistributions in binary form must reproduce the above\r
+   copyright notice, this list of conditions and the following disclaimer\r
+   in the documentation and/or other materials provided with the\r
+   distribution.\r
+\r
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+   You can contact the author at :\r
+   - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+   - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+\r
+/*\r
+Note : this source file requires "lz4_encoder.h"\r
+*/\r
+\r
+//**************************************\r
+// Tuning parameters\r
+//**************************************\r
+// MEMORY_USAGE :\r
+// Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)\r
+// Increasing memory usage improves compression ratio\r
+// Reduced memory usage can improve speed, due to cache effect\r
+// Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache\r
+#define MEMORY_USAGE 14\r
+\r
+// HEAPMODE :\r
+// Select how default compression function will allocate memory for its hash table,\r
+// in memory stack (0:default, fastest), or in memory heap (1:requires memory allocation (malloc)).\r
+// Default allocation strategy is to use stack (HEAPMODE 0)\r
+// Note : explicit functions *_stack* and *_heap* are unaffected by this setting\r
+#define HEAPMODE 0\r
+\r
+// BIG_ENDIAN_NATIVE_BUT_INCOMPATIBLE :\r
+// This will provide a small boost to performance for big endian cpu, but the resulting compressed stream will be incompatible with little-endian CPU.\r
+// You can set this option to 1 in situations where data will remain within closed environment\r
+// This option is useless on Little_Endian CPU (such as x86)\r
+//#define BIG_ENDIAN_NATIVE_BUT_INCOMPATIBLE 1\r
+\r
+\r
+\r
+//**************************************\r
+// CPU Feature Detection\r
+//**************************************\r
+// 32 or 64 bits ?\r
+#if (defined(__x86_64__) || defined(_M_X64) || defined(_WIN64) \\r
+  || defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) \\r
+  || defined(__64BIT__) || defined(_LP64) || defined(__LP64__) \\r
+  || defined(__ia64) || defined(__itanium__) || defined(_M_IA64) )   // Detects 64 bits mode\r
+#  define LZ4_ARCH64 1\r
+#else\r
+#  define LZ4_ARCH64 0\r
+#endif\r
+\r
+// Little Endian or Big Endian ?\r
+// Overwrite the #define below if you know your architecture endianess\r
+#if defined (__GLIBC__)\r
+#  include <endian.h>\r
+#  if (__BYTE_ORDER == __BIG_ENDIAN)\r
+#     define LZ4_BIG_ENDIAN 1\r
+#  endif\r
+#elif (defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN))\r
+#  define LZ4_BIG_ENDIAN 1\r
+#elif defined(__sparc) || defined(__sparc__) \\r
+   || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) \\r
+   || defined(__hpux)  || defined(__hppa) \\r
+   || defined(_MIPSEB) || defined(__s390__)\r
+#  define LZ4_BIG_ENDIAN 1\r
+#else\r
+// Little Endian assumed. PDP Endian and other very rare endian format are unsupported.\r
+#endif\r
+\r
+// Unaligned memory access is automatically enabled for "common" CPU, such as x86.\r
+// For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected\r
+// If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance\r
+#if defined(__ARM_FEATURE_UNALIGNED)\r
+#  define LZ4_FORCE_UNALIGNED_ACCESS 1\r
+#endif\r
+\r
+// Define this parameter if your target system or compiler does not support hardware bit count\r
+#if defined(_MSC_VER) && defined(_WIN32_WCE)            // Visual Studio for Windows CE does not support Hardware bit count\r
+#  define LZ4_FORCE_SW_BITCOUNT\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Compiler Options\r
+//**************************************\r
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99\r
+/* "restrict" is a known keyword */\r
+#else\r
+#  define restrict // Disable restrict\r
+#endif\r
+\r
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)\r
+\r
+#ifdef _MSC_VER    // Visual Studio\r
+#  define forceinline static __forceinline\r
+#  include <intrin.h>                 // For Visual 2005\r
+#  if LZ4_ARCH64     // 64-bits\r
+#    pragma intrinsic(_BitScanForward64) // For Visual 2005\r
+#    pragma intrinsic(_BitScanReverse64) // For Visual 2005\r
+#  else              // 32-bits\r
+#    pragma intrinsic(_BitScanForward)   // For Visual 2005\r
+#    pragma intrinsic(_BitScanReverse)   // For Visual 2005\r
+#  endif\r
+#  pragma warning(disable : 4127)        // disable: C4127: conditional expression is constant\r
+#else\r
+#  ifdef __GNUC__\r
+#    define forceinline static inline __attribute__((always_inline))\r
+#  else\r
+#    define forceinline static inline\r
+#  endif\r
+#endif\r
+\r
+#ifdef _MSC_VER\r
+#  define lz4_bswap16(x) _byteswap_ushort(x)\r
+#else\r
+#  define lz4_bswap16(x) ((unsigned short int) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)))\r
+#endif\r
+\r
+#if (GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__)\r
+#  define expect(expr,value)    (__builtin_expect ((expr),(value)) )\r
+#else\r
+#  define expect(expr,value)    (expr)\r
+#endif\r
+\r
+#define likely(expr)     expect((expr) != 0, 1)\r
+#define unlikely(expr)   expect((expr) != 0, 0)\r
+\r
+\r
+//**************************************\r
+// Includes\r
+//**************************************\r
+#include <stdlib.h>   // for malloc\r
+#include <string.h>   // for memset\r
+#include "lz4.h"\r
+\r
+\r
+//**************************************\r
+// Basic Types\r
+//**************************************\r
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99\r
+# include <stdint.h>\r
+  typedef uint8_t  BYTE;\r
+  typedef uint16_t U16;\r
+  typedef uint32_t U32;\r
+  typedef  int32_t S32;\r
+  typedef uint64_t U64;\r
+#else\r
+  typedef unsigned char       BYTE;\r
+  typedef unsigned short      U16;\r
+  typedef unsigned int        U32;\r
+  typedef   signed int        S32;\r
+  typedef unsigned long long  U64;\r
+#endif\r
+\r
+#if defined(__GNUC__)  && !defined(LZ4_FORCE_UNALIGNED_ACCESS)\r
+#  define _PACKED __attribute__ ((packed))\r
+#else\r
+#  define _PACKED\r
+#endif\r
+\r
+#if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__)\r
+#  ifdef __IBMC__\r
+#    pragma pack(1)\r
+#  else\r
+#    pragma pack(push, 1)\r
+#  endif\r
+#endif\r
+\r
+typedef struct _U16_S { U16 v; } _PACKED U16_S;\r
+typedef struct _U32_S { U32 v; } _PACKED U32_S;\r
+typedef struct _U64_S { U64 v; } _PACKED U64_S;\r
+\r
+#if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__)\r
+#  pragma pack(pop)\r
+#endif\r
+\r
+#define A64(x) (((U64_S *)(x))->v)\r
+#define A32(x) (((U32_S *)(x))->v)\r
+#define A16(x) (((U16_S *)(x))->v)\r
+\r
+\r
+//**************************************\r
+// Constants\r
+//**************************************\r
+#define HASHTABLESIZE (1 << MEMORY_USAGE)\r
+\r
+#define MINMATCH 4\r
+\r
+#define COPYLENGTH 8\r
+#define LASTLITERALS 5\r
+#define MFLIMIT (COPYLENGTH+MINMATCH)\r
+#define MINLENGTH (MFLIMIT+1)\r
+\r
+#define LZ4_64KLIMIT ((1<<16) + (MFLIMIT-1))\r
+#define SKIPSTRENGTH 6     // Increasing this value will make the compression run slower on incompressible data\r
+\r
+#define MAXD_LOG 16\r
+#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)\r
+\r
+#define ML_BITS  4\r
+#define ML_MASK  ((1U<<ML_BITS)-1)\r
+#define RUN_BITS (8-ML_BITS)\r
+#define RUN_MASK ((1U<<RUN_BITS)-1)\r
+\r
+\r
+//**************************************\r
+// Architecture-specific macros\r
+//**************************************\r
+#if LZ4_ARCH64   // 64-bit\r
+#  define STEPSIZE 8\r
+#  define UARCH U64\r
+#  define AARCH A64\r
+#  define LZ4_COPYSTEP(s,d)       A64(d) = A64(s); d+=8; s+=8;\r
+#  define LZ4_COPYPACKET(s,d)     LZ4_COPYSTEP(s,d)\r
+#  define LZ4_SECURECOPY(s,d,e)   if (d<e) LZ4_WILDCOPY(s,d,e)\r
+#  define HTYPE                   U32\r
+#  define INITBASE(base)          const BYTE* const base = ip\r
+#else      // 32-bit\r
+#  define STEPSIZE 4\r
+#  define UARCH U32\r
+#  define AARCH A32\r
+#  define LZ4_COPYSTEP(s,d)       A32(d) = A32(s); d+=4; s+=4;\r
+#  define LZ4_COPYPACKET(s,d)     LZ4_COPYSTEP(s,d); LZ4_COPYSTEP(s,d);\r
+#  define LZ4_SECURECOPY          LZ4_WILDCOPY\r
+#  define HTYPE                   const BYTE*\r
+#  define INITBASE(base)          const int base = 0\r
+#endif\r
+\r
+#if (defined(LZ4_BIG_ENDIAN) && !defined(BIG_ENDIAN_NATIVE_BUT_INCOMPATIBLE))\r
+#  define LZ4_READ_LITTLEENDIAN_16(d,s,p) { U16 v = A16(p); v = lz4_bswap16(v); d = (s) - v; }\r
+#  define LZ4_WRITE_LITTLEENDIAN_16(p,i)  { U16 v = (U16)(i); v = lz4_bswap16(v); A16(p) = v; p+=2; }\r
+#else      // Little Endian\r
+#  define LZ4_READ_LITTLEENDIAN_16(d,s,p) { d = (s) - A16(p); }\r
+#  define LZ4_WRITE_LITTLEENDIAN_16(p,v)  { A16(p) = v; p+=2; }\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Macros\r
+//**************************************\r
+#define LZ4_WILDCOPY(s,d,e)     do { LZ4_COPYPACKET(s,d) } while (d<e);\r
+#define LZ4_BLINDCOPY(s,d,l)    { BYTE* e=(d)+(l); LZ4_WILDCOPY(s,d,e); d=e; }\r
+\r
+\r
+//****************************\r
+// Private functions\r
+//****************************\r
+#if LZ4_ARCH64\r
+\r
+forceinline int LZ4_NbCommonBytes (register U64 val)\r
+{\r
+#if defined(LZ4_BIG_ENDIAN)\r
+    #if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    unsigned long r = 0;\r
+    _BitScanReverse64( &r, val );\r
+    return (int)(r>>3);\r
+    #elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    return (__builtin_clzll(val) >> 3);\r
+    #else\r
+    int r;\r
+    if (!(val>>32)) { r=4; } else { r=0; val>>=32; }\r
+    if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }\r
+    r += (!val);\r
+    return r;\r
+    #endif\r
+#else\r
+    #if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    unsigned long r = 0;\r
+    _BitScanForward64( &r, val );\r
+    return (int)(r>>3);\r
+    #elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    return (__builtin_ctzll(val) >> 3);\r
+    #else\r
+    static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };\r
+    return DeBruijnBytePos[((U64)((val & -val) * 0x0218A392CDABBD3F)) >> 58];\r
+    #endif\r
+#endif\r
+}\r
+\r
+#else\r
+\r
+forceinline int LZ4_NbCommonBytes (register U32 val)\r
+{\r
+#if defined(LZ4_BIG_ENDIAN)\r
+#  if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    unsigned long r = 0;\r
+    _BitScanReverse( &r, val );\r
+    return (int)(r>>3);\r
+#  elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    return (__builtin_clz(val) >> 3);\r
+#  else\r
+    int r;\r
+    if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }\r
+    r += (!val);\r
+    return r;\r
+#  endif\r
+#else\r
+#  if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    unsigned long r;\r
+    _BitScanForward( &r, val );\r
+    return (int)(r>>3);\r
+#  elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    return (__builtin_ctz(val) >> 3);\r
+#  else\r
+    static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };\r
+    return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];\r
+#  endif\r
+#endif\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+//******************************\r
+// Compression functions\r
+//******************************\r
+\r
+/*\r
+int LZ4_compress_stack(\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest'.\r
+Destination buffer must be already allocated, and sized at a minimum of LZ4_compressBound(inputSize).\r
+return : the number of bytes written in buffer 'dest'\r
+*/\r
+#define FUNCTION_NAME LZ4_compress_stack\r
+#include "lz4_encoder.h"\r
+\r
+\r
+/*\r
+int LZ4_compress_stack_limitedOutput(\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize,\r
+                 int maxOutputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.\r
+If it cannot achieve it, compression will stop, and result of the function will be zero.\r
+return : the number of bytes written in buffer 'dest', or 0 if the compression fails\r
+*/\r
+#define FUNCTION_NAME LZ4_compress_stack_limitedOutput\r
+#define LIMITED_OUTPUT\r
+#include "lz4_encoder.h"\r
+\r
+\r
+/*\r
+int LZ4_compress64k_stack(\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest'.\r
+This function compresses better than LZ4_compress_stack(), on the condition that\r
+'inputSize' must be < to LZ4_64KLIMIT, or the function will fail.\r
+Destination buffer must be already allocated, and sized at a minimum of LZ4_compressBound(inputSize).\r
+return : the number of bytes written in buffer 'dest', or 0 if compression fails\r
+*/\r
+#define FUNCTION_NAME LZ4_compress64k_stack\r
+#define COMPRESS_64K\r
+#include "lz4_encoder.h"\r
+\r
+\r
+/*\r
+int LZ4_compress64k_stack_limitedOutput(\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize,\r
+                 int maxOutputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.\r
+This function compresses better than LZ4_compress_stack_limitedOutput(), on the condition that\r
+'inputSize' must be < to LZ4_64KLIMIT, or the function will fail.\r
+If it cannot achieve it, compression will stop, and result of the function will be zero.\r
+return : the number of bytes written in buffer 'dest', or 0 if the compression fails\r
+*/\r
+#define FUNCTION_NAME LZ4_compress64k_stack_limitedOutput\r
+#define COMPRESS_64K\r
+#define LIMITED_OUTPUT\r
+#include "lz4_encoder.h"\r
+\r
+\r
+/*\r
+void* LZ4_createHeapMemory();\r
+int LZ4_freeHeapMemory(void* ctx);\r
+\r
+Used to allocate and free hashTable memory\r
+to be used by the LZ4_compress_heap* family of functions.\r
+LZ4_createHeapMemory() returns NULL is memory allocation fails.\r
+*/\r
+void*    LZ4_create() { return malloc(HASHTABLESIZE); }\r
+unsigned LZ4_create_size() { return HASHTABLESIZE; }\r
+int      LZ4_free(void* ctx) { free(ctx); return 0; }\r
+\r
+\r
+/*\r
+int LZ4_compress_heap(\r
+                 void* ctx,\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest'.\r
+The memory used for compression must be created by LZ4_createHeapMemory() and provided by pointer 'ctx'.\r
+Destination buffer must be already allocated, and sized at a minimum of LZ4_compressBound(inputSize).\r
+return : the number of bytes written in buffer 'dest'\r
+*/\r
+#define FUNCTION_NAME LZ4_compress_heap\r
+#define USE_HEAPMEMORY\r
+#include "lz4_encoder.h"\r
+\r
+\r
+/*\r
+int LZ4_compress_heap_limitedOutput(\r
+                 void* ctx,\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize,\r
+                 int maxOutputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.\r
+If it cannot achieve it, compression will stop, and result of the function will be zero.\r
+The memory used for compression must be created by LZ4_createHeapMemory() and provided by pointer 'ctx'.\r
+return : the number of bytes written in buffer 'dest', or 0 if the compression fails\r
+*/\r
+#define FUNCTION_NAME LZ4_compress_heap_limitedOutput\r
+#define LIMITED_OUTPUT\r
+#define USE_HEAPMEMORY\r
+#include "lz4_encoder.h"\r
+\r
+\r
+/*\r
+int LZ4_compress64k_heap(\r
+                 void* ctx,\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest'.\r
+The memory used for compression must be created by LZ4_createHeapMemory() and provided by pointer 'ctx'.\r
+'inputSize' must be < to LZ4_64KLIMIT, or the function will fail.\r
+Destination buffer must be already allocated, and sized at a minimum of LZ4_compressBound(inputSize).\r
+return : the number of bytes written in buffer 'dest'\r
+*/\r
+#define FUNCTION_NAME LZ4_compress64k_heap\r
+#define COMPRESS_64K\r
+#define USE_HEAPMEMORY\r
+#include "lz4_encoder.h"\r
+\r
+\r
+/*\r
+int LZ4_compress64k_heap_limitedOutput(\r
+                 void* ctx,\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize,\r
+                 int maxOutputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.\r
+If it cannot achieve it, compression will stop, and result of the function will be zero.\r
+The memory used for compression must be created by LZ4_createHeapMemory() and provided by pointer 'ctx'.\r
+'inputSize' must be < to LZ4_64KLIMIT, or the function will fail.\r
+return : the number of bytes written in buffer 'dest', or 0 if the compression fails\r
+*/\r
+#define FUNCTION_NAME LZ4_compress64k_heap_limitedOutput\r
+#define COMPRESS_64K\r
+#define LIMITED_OUTPUT\r
+#define USE_HEAPMEMORY\r
+#include "lz4_encoder.h"\r
+\r
+\r
+int LZ4_compress(const char* source, char* dest, int inputSize)\r
+{\r
+#if HEAPMODE\r
+    void* ctx = LZ4_create();\r
+    int result;\r
+    if (ctx == NULL) return 0;    // Failed allocation => compression not done\r
+    if (inputSize < LZ4_64KLIMIT)\r
+        result = LZ4_compress64k_heap(ctx, source, dest, inputSize);\r
+    else result = LZ4_compress_heap(ctx, source, dest, inputSize);\r
+    LZ4_free(ctx);\r
+    return result;\r
+#else\r
+    if (inputSize < (int)LZ4_64KLIMIT) return LZ4_compress64k_stack(source, dest, inputSize);\r
+    return LZ4_compress_stack(source, dest, inputSize);\r
+#endif\r
+}\r
+\r
+\r
+int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)\r
+{\r
+#if HEAPMODE\r
+    void* ctx = LZ4_create();\r
+    int result;\r
+    if (ctx == NULL) return 0;    // Failed allocation => compression not done\r
+    if (inputSize < LZ4_64KLIMIT)\r
+        result = LZ4_compress64k_heap_limitedOutput(ctx, source, dest, inputSize, maxOutputSize);\r
+    else result = LZ4_compress_heap_limitedOutput(ctx, source, dest, inputSize, maxOutputSize);\r
+    LZ4_free(ctx);\r
+    return result;\r
+#else\r
+    if (inputSize < (int)LZ4_64KLIMIT) return LZ4_compress64k_stack_limitedOutput(source, dest, inputSize, maxOutputSize);\r
+    return LZ4_compress_stack_limitedOutput(source, dest, inputSize, maxOutputSize);\r
+#endif\r
+}\r
+\r
+\r
+//****************************\r
+// Decompression functions\r
+//****************************\r
+\r
+typedef enum { noPrefix = 0, withPrefix = 1 } prefix64k_directive;\r
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } end_directive;\r
+typedef enum { full = 0, partial = 1 } exit_directive;\r
+\r
+\r
+// This generic decompression function cover all use cases.\r
+// It shall be instanciated several times, using different sets of directives\r
+// Note that it is essential this generic function is really inlined,\r
+// in order to remove useless branches during compilation optimisation.\r
+forceinline int LZ4_decompress_generic(\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize,          //\r
+                 int outputSize,         // OutputSize must be != 0; if endOnInput==endOnInputSize, this value is the max size of Output Buffer.\r
+\r
+                 int endOnInput,         // endOnOutputSize, endOnInputSize\r
+                 int prefix64k,          // noPrefix, withPrefix\r
+                 int partialDecoding,    // full, partial\r
+                 int targetOutputSize    // only used if partialDecoding==partial\r
+                 )\r
+{\r
+    // Local Variables\r
+    const BYTE* restrict ip = (const BYTE*) source;\r
+    const BYTE* ref;\r
+    const BYTE* const iend = ip + inputSize;\r
+\r
+    BYTE* op = (BYTE*) dest;\r
+    BYTE* const oend = op + outputSize;\r
+    BYTE* cpy;\r
+    BYTE* oexit = op + targetOutputSize;\r
+\r
+    size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0};\r
+#if LZ4_ARCH64\r
+    size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3};\r
+#endif\r
+\r
+\r
+    // Special case\r
+    if ((partialDecoding) && (oexit> oend-MFLIMIT)) oexit = oend-MFLIMIT;                        // targetOutputSize too high => decode everything\r
+    if ((endOnInput) && unlikely(outputSize==0)) return ((inputSize==1) && (*ip==0)) ? 0 : -1;   // Empty output buffer\r
+    if ((!endOnInput) && unlikely(outputSize==0)) return (*ip==0?1:-1);\r
+\r
+\r
+    // Main Loop\r
+    while (1)\r
+    {\r
+        unsigned token;\r
+        size_t length;\r
+\r
+        // get runlength\r
+        token = *ip++;\r
+        if ((length=(token>>ML_BITS)) == RUN_MASK)\r
+        {\r
+            unsigned s=255;\r
+            while (((endOnInput)?ip<iend:1) && (s==255))\r
+            {\r
+                s = *ip++;\r
+                length += s;\r
+            }\r
+        }\r
+\r
+        // copy literals\r
+        cpy = op+length;\r
+        if (((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )\r
+            || ((!endOnInput) && (cpy>oend-COPYLENGTH)))\r
+        {\r
+            if (partialDecoding)\r
+            {\r
+                if (cpy > oend) goto _output_error;                            // Error : write attempt beyond end of output buffer\r
+                if ((endOnInput) && (ip+length > iend)) goto _output_error;    // Error : read attempt beyond end of input buffer\r
+            }\r
+            else\r
+            {\r
+                if ((!endOnInput) && (cpy != oend)) goto _output_error;        // Error : block decoding must stop exactly there\r
+                if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error;   // Error : input must be consumed\r
+            }\r
+            memcpy(op, ip, length);\r
+            ip += length;\r
+            op += length;\r
+            break;                                       // Necessarily EOF, due to parsing restrictions\r
+        }\r
+        LZ4_WILDCOPY(ip, op, cpy); ip -= (op-cpy); op = cpy;\r
+\r
+        // get offset\r
+        LZ4_READ_LITTLEENDIAN_16(ref,cpy,ip); ip+=2;\r
+        if ((prefix64k==noPrefix) && unlikely(ref < (BYTE* const)dest)) goto _output_error;   // Error : offset outside destination buffer\r
+\r
+        // get matchlength\r
+        if ((length=(token&ML_MASK)) == ML_MASK)\r
+        {\r
+            for ( ; (!endOnInput) || (ip<iend-(LASTLITERALS+1)) ; )   // Ensure enough bytes remain for LASTLITERALS + token\r
+            {\r
+                unsigned s = *ip++;\r
+                length += s;\r
+                if (s==255) continue;\r
+                break;\r
+            }\r
+        }\r
+\r
+        // copy repeated sequence\r
+        if unlikely((op-ref)<STEPSIZE)\r
+        {\r
+#if LZ4_ARCH64\r
+            size_t dec64 = dec64table[op-ref];\r
+#else\r
+            const size_t dec64 = 0;\r
+#endif\r
+            op[0] = ref[0];\r
+            op[1] = ref[1];\r
+            op[2] = ref[2];\r
+            op[3] = ref[3];\r
+            op += 4, ref += 4; ref -= dec32table[op-ref];\r
+            A32(op) = A32(ref);\r
+            op += STEPSIZE-4; ref -= dec64;\r
+        } else { LZ4_COPYSTEP(ref,op); }\r
+        cpy = op + length - (STEPSIZE-4);\r
+\r
+        if unlikely(cpy>oend-(COPYLENGTH)-(STEPSIZE-4))\r
+        {\r
+            if (cpy > oend-LASTLITERALS) goto _output_error;    // Error : last 5 bytes must be literals\r
+            LZ4_SECURECOPY(ref, op, (oend-COPYLENGTH));\r
+            while(op<cpy) *op++=*ref++;\r
+            op=cpy;\r
+            continue;\r
+        }\r
+        LZ4_WILDCOPY(ref, op, cpy);\r
+        op=cpy;   // correction\r
+    }\r
+\r
+    // end of decoding\r
+    if (endOnInput)\r
+       return (int) (((char*)op)-dest);     // Nb of output bytes decoded\r
+    else\r
+       return (int) (((char*)ip)-source);   // Nb of input bytes read\r
+\r
+    // Overflow error detected\r
+_output_error:\r
+    return (int) (-(((char*)ip)-source))-1;\r
+}\r
+\r
+\r
+int LZ4_decompress_safe(const char* source, char* dest, int inputSize, int maxOutputSize)\r
+{\r
+    return LZ4_decompress_generic(source, dest, inputSize, maxOutputSize, endOnInputSize, noPrefix, full, 0);\r
+}\r
+\r
+int LZ4_decompress_fast(const char* source, char* dest, int outputSize)\r
+{\r
+    return LZ4_decompress_generic(source, dest, 0, outputSize, endOnOutputSize, noPrefix, full, 0);\r
+}\r
+\r
+int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int inputSize, int maxOutputSize)\r
+{\r
+    return LZ4_decompress_generic(source, dest, inputSize, maxOutputSize, endOnInputSize, withPrefix, full, 0);\r
+}\r
+\r
+int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int outputSize)\r
+{\r
+    return LZ4_decompress_generic(source, dest, 0, outputSize, endOnOutputSize, withPrefix, full, 0);\r
+}\r
+\r
+int LZ4_decompress_safe_partial(const char* source, char* dest, int inputSize, int targetOutputSize, int maxOutputSize)\r
+{\r
+    return LZ4_decompress_generic(source, dest, inputSize, maxOutputSize, endOnInputSize, noPrefix, partial, targetOutputSize);\r
+}\r
+\r
diff --git a/silo/third-party/lz4/lz4.h b/silo/third-party/lz4/lz4.h
new file mode 100644 (file)
index 0000000..70c1c4a
--- /dev/null
@@ -0,0 +1,200 @@
+/*\r
+   LZ4 - Fast LZ compression algorithm\r
+   Header File\r
+   Copyright (C) 2011-2013, Yann Collet.\r
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+   Redistribution and use in source and binary forms, with or without\r
+   modification, are permitted provided that the following conditions are\r
+   met:\r
+\r
+       * Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+       * Redistributions in binary form must reproduce the above\r
+   copyright notice, this list of conditions and the following disclaimer\r
+   in the documentation and/or other materials provided with the\r
+   distribution.\r
+\r
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+   You can contact the author at :\r
+   - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+   - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+#pragma once\r
+\r
+#if defined (__cplusplus)\r
+extern "C" {\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Compiler Options\r
+//**************************************\r
+#if defined(_MSC_VER) && !defined(__cplusplus)   // Visual Studio\r
+#  define inline __inline           // Visual C is not C99, but supports some kind of inline\r
+#endif\r
+\r
+\r
+//****************************\r
+// Simple Functions\r
+//****************************\r
+\r
+int LZ4_compress        (const char* source, char* dest, int inputSize);\r
+int LZ4_decompress_safe (const char* source, char* dest, int inputSize, int maxOutputSize);\r
+\r
+/*\r
+LZ4_compress() :\r
+    Compresses 'inputSize' bytes from 'source' into 'dest'.\r
+    Destination buffer must be already allocated,\r
+    and must be sized to handle worst cases situations (input data not compressible)\r
+    Worst case size evaluation is provided by function LZ4_compressBound()\r
+    inputSize : Max supported value is ~1.9GB\r
+    return : the number of bytes written in buffer dest\r
+             or 0 if the compression fails\r
+\r
+LZ4_decompress_safe() :\r
+    maxOutputSize : is the size of the destination buffer (which must be already allocated)\r
+    return : the number of bytes decoded in the destination buffer (necessarily <= maxOutputSize)\r
+             If the source stream is detected malformed, the function will stop decoding and return a negative result.\r
+             This function is protected against buffer overflow exploits (never writes outside of output buffer, and never reads outside of input buffer). Therefore, it is protected against malicious data packets\r
+*/\r
+\r
+\r
+//****************************\r
+// Advanced Functions\r
+//****************************\r
+\r
+static inline int LZ4_compressBound(int isize)   { return ((isize) + ((isize)/255) + 16); }\r
+#define           LZ4_COMPRESSBOUND(    isize)            ((isize) + ((isize)/255) + 16)\r
+\r
+/*\r
+LZ4_compressBound() :\r
+    Provides the maximum size that LZ4 may output in a "worst case" scenario (input data not compressible)\r
+    primarily useful for memory allocation of output buffer.\r
+    inline function is recommended for the general case,\r
+    macro is also provided when result needs to be evaluated at compilation (such as table size allocation).\r
+\r
+    isize  : is the input size. Max supported value is ~1.9GB\r
+    return : maximum output size in a "worst case" scenario\r
+    note : this function is limited by "int" range (2^31-1)\r
+*/\r
+\r
+static inline int\r
+LZ4_compressBoundInv(int bound)\r
+{\r
+  if (bound < 16)\r
+    return 0;\r
+  return 255 * (bound - 16) / 256;\r
+}\r
+\r
+/*\r
+LZ4_compressBoundInv() :\r
+    Given a output buffer size b, returns the maximum number of bytes\r
+    which can be compressed and still guarantee compressed output <= b\r
+\r
+    returns 0 if bound is too small to compress anything useful\r
+*/\r
+\r
+int LZ4_compress_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize);\r
+\r
+/*\r
+LZ4_compress_limitedOutput() :\r
+    Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.\r
+    If it cannot achieve it, compression will stop, and result of the function will be zero.\r
+    This function never writes outside of provided output buffer.\r
+\r
+    inputSize  : Max supported value is ~1.9GB\r
+    maxOutputSize : is the size of the destination buffer (which must be already allocated)\r
+    return : the number of bytes written in buffer 'dest'\r
+             or 0 if the compression fails\r
+*/\r
+\r
+\r
+int LZ4_decompress_fast (const char* source, char* dest, int outputSize);\r
+\r
+/*\r
+LZ4_decompress_fast() :\r
+    outputSize : is the original (uncompressed) size\r
+    return : the number of bytes read from the source buffer (in other words, the compressed size)\r
+             If the source stream is malformed, the function will stop decoding and return a negative result.\r
+    note : This function is a bit faster than LZ4_decompress_safe()\r
+           This function never writes outside of output buffers, and never read before input buffer, but may read beyond input buffer (since it doesn't know its size) in case of malicious data packet.\r
+           Use this function preferably into a trusted environment (data to decode comes from a trusted source).\r
+           Destination buffer must be already allocated. Its size must be a minimum of 'outputSize' bytes.\r
+*/\r
+\r
+int LZ4_decompress_safe_partial (const char* source, char* dest, int inputSize, int targetOutputSize, int maxOutputSize);\r
+\r
+/*\r
+LZ4_decompress_safe_partial() :\r
+    This function decompress a compressed block of size 'inputSize' at position 'source'\r
+    into output buffer 'dest' of size 'maxOutputSize'.\r
+    The function stops decompressing operation as soon as 'targetOutputSize' has been reached,\r
+    reducing decompression time.\r
+    return : the number of bytes decoded in the destination buffer (necessarily <= maxOutputSize)\r
+       Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.\r
+             Always control how many bytes were decoded.\r
+             If the source stream is malformed, the function will stop decoding and return a negative result.\r
+             This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets\r
+*/\r
+\r
+\r
+int LZ4_decompress_safe_withPrefix64k (const char* source, char* dest, int inputSize, int maxOutputSize);\r
+int LZ4_decompress_fast_withPrefix64k (const char* source, char* dest, int outputSize);\r
+\r
+/*\r
+*_withPrefix64k() :\r
+    These decoding functions work the same as their "normal name" versions,\r
+    but will potentially use up to 64KB of data in front of 'char* dest'.\r
+    These functions are used for decoding inter-dependant blocks.\r
+*/\r
+\r
+//****************************\r
+// Exposed Functions\r
+//****************************\r
+\r
+void*    LZ4_create();\r
+unsigned LZ4_create_size();\r
+int      LZ4_free(void* ctx);\r
+\r
+int LZ4_compress_heap(\r
+                 void* ctx,\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize);\r
+\r
+int LZ4_compress_heap_limitedOutput(\r
+                 void* ctx,\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize,\r
+                 int maxOutputSize);\r
+\r
+//****************************\r
+// Obsolete Functions\r
+//****************************\r
+\r
+static inline int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); }\r
+static inline int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); }\r
+\r
+/*\r
+These functions are deprecated and should no longer be used.\r
+They are provided here for compatibility with existing user programs.\r
+*/\r
+\r
+\r
+\r
+#if defined (__cplusplus)\r
+}\r
+#endif\r
diff --git a/silo/third-party/lz4/lz4.o b/silo/third-party/lz4/lz4.o
new file mode 100644 (file)
index 0000000..f438ab4
Binary files /dev/null and b/silo/third-party/lz4/lz4.o differ
diff --git a/silo/third-party/lz4/lz4_encoder.h b/silo/third-party/lz4/lz4_encoder.h
new file mode 100644 (file)
index 0000000..c18cfc6
--- /dev/null
@@ -0,0 +1,262 @@
+/*\r
+   LZ4 Encoder - Part of LZ4 compression algorithm\r
+   Copyright (C) 2011-2013, Yann Collet.\r
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+   Redistribution and use in source and binary forms, with or without\r
+   modification, are permitted provided that the following conditions are\r
+   met:\r
+\r
+       * Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+       * Redistributions in binary form must reproduce the above\r
+   copyright notice, this list of conditions and the following disclaimer\r
+   in the documentation and/or other materials provided with the\r
+   distribution.\r
+\r
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+   You can contact the author at :\r
+   - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+   - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+\r
+/* lz4_encoder.h must be included into lz4.c\r
+   The objective of this file is to create a single LZ4 compression function source\r
+   which will be instanciated multiple times with minor variations\r
+   depending on a set of #define.\r
+*/\r
+\r
+\r
+\r
+//****************************\r
+// Check required defines\r
+//****************************\r
+\r
+#ifndef FUNCTION_NAME\r
+#  error "FUNTION_NAME is not defined"\r
+#endif\r
+\r
+\r
+//****************************\r
+// Local definitions\r
+//****************************\r
+\r
+#ifdef COMPRESS_64K\r
+#  define HASHLOG (MEMORY_USAGE-1)\r
+#  define CURRENT_H_TYPE U16\r
+#  define CURRENTBASE(base) const BYTE* const base = ip\r
+#else\r
+#  define HASHLOG (MEMORY_USAGE-2)\r
+#  define CURRENT_H_TYPE HTYPE\r
+#  define CURRENTBASE(base) INITBASE(base)\r
+#endif\r
+\r
+#define HASHTABLE_NBCELLS  (1U<<HASHLOG)\r
+#define LZ4_HASH(i)        (((i) * 2654435761U) >> ((MINMATCH*8)-HASHLOG))\r
+#define LZ4_HASHVALUE(p)   LZ4_HASH(A32(p))\r
+\r
+\r
+\r
+//****************************\r
+// Function code\r
+//****************************\r
+\r
+int FUNCTION_NAME(\r
+#ifdef USE_HEAPMEMORY\r
+                 void* ctx,\r
+#endif\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize\r
+#ifdef LIMITED_OUTPUT\r
+                ,int maxOutputSize\r
+#endif\r
+                 )\r
+{\r
+#ifdef USE_HEAPMEMORY\r
+    CURRENT_H_TYPE* HashTable = (CURRENT_H_TYPE*)ctx;\r
+#else\r
+    CURRENT_H_TYPE HashTable[HASHTABLE_NBCELLS] = {0};\r
+#endif\r
+\r
+    const BYTE* ip = (BYTE*) source;\r
+    CURRENTBASE(base);\r
+    const BYTE* anchor = ip;\r
+    const BYTE* const iend = ip + inputSize;\r
+    const BYTE* const mflimit = iend - MFLIMIT;\r
+#define matchlimit (iend - LASTLITERALS)\r
+\r
+    BYTE* op = (BYTE*) dest;\r
+#ifdef LIMITED_OUTPUT\r
+    BYTE* const oend = op + maxOutputSize;\r
+#endif\r
+\r
+    int length;\r
+    const int skipStrength = SKIPSTRENGTH;\r
+    U32 forwardH;\r
+\r
+\r
+    // Init\r
+    if (inputSize<MINLENGTH) goto _last_literals;\r
+#ifdef COMPRESS_64K\r
+    if (inputSize>LZ4_64KLIMIT) return 0;   // Size too large (not within 64K limit)\r
+#endif\r
+#ifdef USE_HEAPMEMORY\r
+    memset((void*)HashTable, 0, HASHTABLESIZE);\r
+#endif\r
+\r
+    // First Byte\r
+    HashTable[LZ4_HASHVALUE(ip)] = (CURRENT_H_TYPE)(ip - base);\r
+    ip++; forwardH = LZ4_HASHVALUE(ip);\r
+\r
+    // Main Loop\r
+    for ( ; ; )\r
+    {\r
+        int findMatchAttempts = (1U << skipStrength) + 3;\r
+        const BYTE* forwardIp = ip;\r
+        const BYTE* ref;\r
+        BYTE* token;\r
+\r
+        // Find a match\r
+        do {\r
+            U32 h = forwardH;\r
+            int step = findMatchAttempts++ >> skipStrength;\r
+            ip = forwardIp;\r
+            forwardIp = ip + step;\r
+\r
+            if unlikely(forwardIp > mflimit) { goto _last_literals; }\r
+\r
+            forwardH = LZ4_HASHVALUE(forwardIp);\r
+            ref = base + HashTable[h];\r
+            HashTable[h] = (CURRENT_H_TYPE)(ip - base);\r
+\r
+        } while ((ref < ip - MAX_DISTANCE) || (A32(ref) != A32(ip)));\r
+\r
+        // Catch up\r
+        while ((ip>anchor) && (ref>(BYTE*)source) && unlikely(ip[-1]==ref[-1])) { ip--; ref--; }\r
+\r
+        // Encode Literal length\r
+        length = (int)(ip - anchor);\r
+        token = op++;\r
+#ifdef LIMITED_OUTPUT\r
+        if unlikely(op + length + (2 + 1 + LASTLITERALS) + (length>>8) > oend) return 0;   // Check output limit\r
+#endif\r
+        if (length>=(int)RUN_MASK)\r
+        {\r
+            int len = length-RUN_MASK;\r
+            *token=(RUN_MASK<<ML_BITS);\r
+            for(; len >= 255 ; len-=255) *op++ = 255;\r
+            *op++ = (BYTE)len;\r
+        }\r
+        else *token = (BYTE)(length<<ML_BITS);\r
+\r
+        // Copy Literals\r
+        LZ4_BLINDCOPY(anchor, op, length);\r
+\r
+_next_match:\r
+        // Encode Offset\r
+        LZ4_WRITE_LITTLEENDIAN_16(op,(U16)(ip-ref));\r
+\r
+        // Start Counting\r
+        ip+=MINMATCH; ref+=MINMATCH;    // MinMatch already verified\r
+        anchor = ip;\r
+        while likely(ip<matchlimit-(STEPSIZE-1))\r
+        {\r
+            UARCH diff = AARCH(ref) ^ AARCH(ip);\r
+            if (!diff) { ip+=STEPSIZE; ref+=STEPSIZE; continue; }\r
+            ip += LZ4_NbCommonBytes(diff);\r
+            goto _endCount;\r
+        }\r
+        if (LZ4_ARCH64) if ((ip<(matchlimit-3)) && (A32(ref) == A32(ip))) { ip+=4; ref+=4; }\r
+        if ((ip<(matchlimit-1)) && (A16(ref) == A16(ip))) { ip+=2; ref+=2; }\r
+        if ((ip<matchlimit) && (*ref == *ip)) ip++;\r
+_endCount:\r
+\r
+        // Encode MatchLength\r
+        length = (int)(ip - anchor);\r
+#ifdef LIMITED_OUTPUT\r
+        if unlikely(op + (1 + LASTLITERALS) + (length>>8) > oend) return 0;    // Check output limit\r
+#endif\r
+        if (length>=(int)ML_MASK)\r
+        {\r
+            *token += ML_MASK;\r
+            length -= ML_MASK;\r
+            for (; length > 509 ; length-=510) { *op++ = 255; *op++ = 255; }\r
+            if (length >= 255) { length-=255; *op++ = 255; }\r
+            *op++ = (BYTE)length;\r
+        }\r
+        else *token += (BYTE)(length);\r
+\r
+        // Test end of chunk\r
+        if (ip > mflimit) { anchor = ip;  break; }\r
+\r
+        // Fill table\r
+        HashTable[LZ4_HASHVALUE(ip-2)] = (CURRENT_H_TYPE)(ip - 2 - base);\r
+\r
+        // Test next position\r
+        ref = base + HashTable[LZ4_HASHVALUE(ip)];\r
+        HashTable[LZ4_HASHVALUE(ip)] = (CURRENT_H_TYPE)(ip - base);\r
+        if ((ref >= ip - MAX_DISTANCE) && (A32(ref) == A32(ip))) { token = op++; *token=0; goto _next_match; }\r
+\r
+        // Prepare next loop\r
+        anchor = ip++;\r
+        forwardH = LZ4_HASHVALUE(ip);\r
+    }\r
+\r
+_last_literals:\r
+    // Encode Last Literals\r
+    {\r
+        int lastRun = (int)(iend - anchor);\r
+#ifdef LIMITED_OUTPUT\r
+        if (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) return 0;  // Check output limit\r
+#endif\r
+        if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<<ML_BITS); lastRun-=RUN_MASK; for(; lastRun >= 255 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; }\r
+        else *op++ = (BYTE)(lastRun<<ML_BITS);\r
+        memcpy(op, anchor, iend - anchor);\r
+        op += iend-anchor;\r
+    }\r
+\r
+    // End\r
+    return (int) (((char*)op)-dest);\r
+}\r
+\r
+\r
+\r
+//****************************\r
+// Clean defines\r
+//****************************\r
+\r
+// Required defines\r
+#undef FUNCTION_NAME\r
+\r
+// Locally Generated\r
+#undef HASHLOG\r
+#undef HASHTABLE_NBCELLS\r
+#undef LZ4_HASH\r
+#undef LZ4_HASHVALUE\r
+#undef CURRENT_H_TYPE\r
+#undef CURRENTBASE\r
+\r
+// Optional defines\r
+#ifdef LIMITED_OUTPUT\r
+#undef LIMITED_OUTPUT\r
+#endif\r
+\r
+#ifdef USE_HEAPMEMORY\r
+#undef USE_HEAPMEMORY\r
+#endif\r
+\r
+#ifdef COMPRESS_64K\r
+#undef COMPRESS_64K\r
+#endif\r
diff --git a/silo/third-party/lz4/lz4_format_description.txt b/silo/third-party/lz4/lz4_format_description.txt
new file mode 100644 (file)
index 0000000..888c57b
--- /dev/null
@@ -0,0 +1,120 @@
+LZ4 Format Description\r
+Last revised: 2012-02-27\r
+Author : Y. Collet\r
+\r
+\r
+\r
+This small specification intents to provide enough information\r
+to anyone willing to produce LZ4-compatible compressed data blocks\r
+using any programming language.\r
+\r
+LZ4 is an LZ77-type compressor with a fixed, byte-oriented encoding.\r
+The most important design principle behind LZ4 is simplicity.\r
+It helps to create an easy to read and maintain source code.\r
+It also helps later on for optimisations, compactness, and speed.\r
+There is no entropy encoder backend nor framing layer.\r
+The latter is assumed to be handled by other parts of the system.\r
+\r
+This document only describes the format,\r
+not how the LZ4 compressor nor decompressor actually work.\r
+The correctness of the decompressor should not depend\r
+on implementation details of the compressor, and vice versa.\r
+\r
+\r
+\r
+-- Compressed block format --\r
+\r
+An LZ4 compressed block is composed of sequences.\r
+Schematically, a sequence is a suite of literals, followed by a match copy.\r
+\r
+Each sequence starts with a token.\r
+The token is a one byte value, separated into two 4-bits fields.\r
+Therefore each field ranges from 0 to 15.\r
+\r
+\r
+The first field uses the 4 high-bits of the token.\r
+It provides the length of literals to follow.\r
+(Note : a literal is a not-compressed byte).\r
+If the field value is 0, then there is no literal.\r
+If it is 15, then we need to add some more bytes to indicate the full length.\r
+Each additionnal byte then represent a value from 0 to 255,\r
+which is added to the previous value to produce a total length.\r
+When the byte value is 255, another byte is output.\r
+There can be any number of bytes following the token. There is no "size limit".\r
+(Sidenote this is why a not-compressible input block is expanded by 0.4%).\r
+\r
+Example 1 : A length of 48 will be represented as :\r
+- 15 : value for the 4-bits High field\r
+- 33 : (=48-15) remaining length to reach 48\r
+\r
+Example 2 : A length of 280 will be represented as :\r
+- 15  : value for the 4-bits High field\r
+- 255 : following byte is maxed, since 280-15 >= 255\r
+- 10  : (=280 - 15 - 255) ) remaining length to reach 280\r
+\r
+Example 3 : A length of 15 will be represented as :\r
+- 15 : value for the 4-bits High field\r
+- 0  : (=15-15) yes, the zero must be output\r
+\r
+Following the token and optional length bytes, are the literals themselves.\r
+They are exactly as numerous as previously decoded (length of literals).\r
+It's possible that there are zero literal.\r
+\r
+\r
+Following the literals is the match copy operation.\r
+\r
+It starts by the offset.\r
+This is a 2 bytes value, in little endian format.\r
+\r
+The offset represents the position of the match to be copied from.\r
+1 means "current position - 1 byte".\r
+The maximum offset value is 65535, 65536 cannot be coded.\r
+Note that 0 is an invalid value, not used. \r
+\r
+Then we need to extract the match length.\r
+For this, we use the second token field, the low 4-bits.\r
+Value, obviously, ranges from 0 to 15.\r
+However here, 0 means that the copy operation will be minimal.\r
+The minimum length of a match, called minmatch, is 4. \r
+As a consequence, a 0 value means 4 bytes, and a value of 15 means 19+ bytes.\r
+Similar to literal length, on reaching the highest possible value (15), \r
+we output additional bytes, one at a time, with values ranging from 0 to 255.\r
+They are added to total to provide the final match length.\r
+A 255 value means there is another byte to read and add.\r
+There is no limit to the number of optional bytes that can be output this way.\r
+(This points towards a maximum achievable compression ratio of ~250).\r
+\r
+With the offset and the matchlength,\r
+the decoder can now proceed to copy the data from the already decoded buffer.\r
+On decoding the matchlength, we reach the end of the compressed sequence,\r
+and therefore start another one.\r
+\r
+\r
+-- Parsing restrictions --\r
+\r
+There are specific parsing rules to respect in order to remain compatible\r
+with assumptions made by the decoder :\r
+1) The last 5 bytes are always literals\r
+2) The last match must start at least 12 bytes before end of block\r
+Consequently, a block with less than 13 bytes cannot be compressed.\r
+These rules are in place to ensure that the decoder\r
+will never read beyond the input buffer, nor write beyond the output buffer.\r
+\r
+Note that the last sequence is also incomplete,\r
+and stops right after literals.\r
+\r
+\r
+-- Additional notes --\r
+\r
+There is no assumption nor limits to the way the compressor\r
+searches and selects matches within the source data block.\r
+It could be a fast scan, a multi-probe, a full search using BST,\r
+standard hash chains or MMC, well whatever.\r
+\r
+Advanced parsing strategies can also be implemented, such as lazy match,\r
+or full optimal parsing.\r
+\r
+All these trade-off offer distinctive speed/memory/compression advantages.\r
+Whatever the method used by the compressor, its result will be decodable\r
+by any LZ4 decoder if it follows the format specification described above.\r
+\r
diff --git a/silo/third-party/lz4/lz4c.c b/silo/third-party/lz4/lz4c.c
new file mode 100644 (file)
index 0000000..8da1e5f
--- /dev/null
@@ -0,0 +1,1066 @@
+/*\r
+  LZ4c - LZ4 Compression CLI program \r
+  Copyright (C) Yann Collet 2011-2013\r
+  GPL v2 License\r
+\r
+  This program is free software; you can redistribute it and/or modify\r
+  it under the terms of the GNU General Public License as published by\r
+  the Free Software Foundation; either version 2 of the License, or\r
+  (at your option) any later version.\r
+\r
+  This program is distributed in the hope that it will be useful,\r
+  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+  GNU General Public License for more details.\r
+\r
+  You should have received a copy of the GNU General Public License along\r
+  with this program; if not, write to the Free Software Foundation, Inc.,\r
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+  You can contact the author at :\r
+  - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+  - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+/*\r
+  Note : this is stand-alone program.\r
+  It is not part of LZ4 compression library, it is a user program of LZ4 library.\r
+  The license of LZ4 library is BSD.\r
+  The license of xxHash library is BSD.\r
+  The license of this compression CLI program is GPLv2.\r
+*/\r
+\r
+//**************************************\r
+// Compiler Options\r
+//**************************************\r
+// Disable some Visual warning messages\r
+#ifdef _MSC_VER  // Visual Studio\r
+#  define _CRT_SECURE_NO_WARNINGS\r
+#  define _CRT_SECURE_NO_DEPRECATE     // VS2005\r
+#  pragma warning(disable : 4127)      // disable: C4127: conditional expression is constant\r
+#endif\r
+\r
+\r
+//****************************\r
+// Includes\r
+//****************************\r
+#include <stdio.h>    // fprintf, fopen, fread, _fileno(?)\r
+#include <stdlib.h>   // malloc\r
+#include <string.h>   // strcmp\r
+#include <time.h>     // clock\r
+#ifdef _WIN32\r
+#include <io.h>       // _setmode\r
+#include <fcntl.h>    // _O_BINARY\r
+#endif\r
+#include "lz4.h"\r
+#include "lz4hc.h"\r
+#include "bench.h"\r
+#include "xxhash.h"\r
+\r
+\r
+//**************************************\r
+// Compiler-specific functions\r
+//**************************************\r
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)\r
+\r
+#if defined(_MSC_VER)    // Visual Studio\r
+#  define swap32 _byteswap_ulong\r
+#elif GCC_VERSION >= 403\r
+#  define swap32 __builtin_bswap32\r
+#else\r
+  static inline unsigned int swap32(unsigned int x) {\r
+    return     ((x << 24) & 0xff000000 ) |\r
+        ((x <<  8) & 0x00ff0000 ) |\r
+        ((x >>  8) & 0x0000ff00 ) |\r
+        ((x >> 24) & 0x000000ff );\r
+  }\r
+#endif\r
+\r
+\r
+//****************************\r
+// Constants\r
+//****************************\r
+#define COMPRESSOR_NAME "LZ4 Compression CLI"\r
+#define COMPRESSOR_VERSION ""\r
+#define COMPILED __DATE__\r
+#define AUTHOR "Yann Collet"\r
+#define EXTENSION ".lz4"\r
+#define WELCOME_MESSAGE "*** %s %s, by %s (%s) ***\n", COMPRESSOR_NAME, COMPRESSOR_VERSION, AUTHOR, COMPILED\r
+\r
+#define KB *(1U<<10)\r
+#define MB *(1U<<20)\r
+#define GB *(1U<<30)\r
+\r
+#define _1BIT  0x01\r
+#define _2BITS 0x03\r
+#define _3BITS 0x07\r
+#define _4BITS 0x0F\r
+#define _8BITS 0xFF\r
+\r
+#define MAGICNUMBER_SIZE 4\r
+#define LZ4S_MAGICNUMBER   0x184D2204\r
+#define LZ4S_SKIPPABLE0    0x184D2A50\r
+#define LZ4S_SKIPPABLEMASK 0xFFFFFFF0\r
+#define LEGACY_MAGICNUMBER 0x184C2102\r
+\r
+#define CACHELINE 64\r
+#define LEGACY_BLOCKSIZE   (8 MB)\r
+#define MIN_STREAM_BUFSIZE (1 MB + 64 KB)\r
+#define LZ4S_BLOCKSIZEID_DEFAULT 7\r
+#define LZ4S_CHECKSUM_SEED 0\r
+#define LZ4S_EOS 0\r
+#define LZ4S_MAXHEADERSIZE (4+2+8+4+1)\r
+\r
+\r
+//**************************************\r
+// Architecture Macros\r
+//**************************************\r
+static const int one = 1;\r
+#define CPU_LITTLE_ENDIAN   (*(char*)(&one))\r
+#define CPU_BIG_ENDIAN      (!CPU_LITTLE_ENDIAN)\r
+#define LITTLE_ENDIAN_32(i) (CPU_LITTLE_ENDIAN?(i):swap32(i))\r
+\r
+\r
+//**************************************\r
+// Macros\r
+//**************************************\r
+#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)\r
+\r
+\r
+//**************************************\r
+// Special input/output\r
+//**************************************\r
+#define NULL_INPUT "null"\r
+char stdinmark[] = "stdin";\r
+char stdoutmark[] = "stdout";\r
+#ifdef _WIN32\r
+char nulmark[] = "nul";\r
+#else\r
+char nulmark[] = "/dev/null";\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Local Parameters\r
+//**************************************\r
+static int overwrite = 0;\r
+static int blockSizeId = LZ4S_BLOCKSIZEID_DEFAULT;\r
+static int blockChecksum = 0;\r
+static int streamChecksum = 1;\r
+static int blockIndependence = 1;\r
+\r
+//**************************************\r
+// Exceptions\r
+//**************************************\r
+#define DEBUG 0\r
+#define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);\r
+#define EXM_THROW(error, ...)                                             \\r
+{                                                                         \\r
+    DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \\r
+    DISPLAY("Error %i : ", error);                                        \\r
+    DISPLAY(__VA_ARGS__);                                                 \\r
+    DISPLAY("\n");                                                        \\r
+    exit(error);                                                          \\r
+}\r
+\r
+\r
+\r
+//****************************\r
+// Functions\r
+//****************************\r
+int usage(char* exename)\r
+{\r
+    DISPLAY( "Usage :\n");\r
+    DISPLAY( "      %s [arg] input [output]\n", exename);\r
+    DISPLAY( "Arguments :\n");\r
+    DISPLAY( " -c0/-c : Fast compression (default) \n");\r
+    DISPLAY( " -c1/-hc: High compression \n");\r
+    DISPLAY( " -d     : decompression \n");\r
+    DISPLAY( " -y     : overwrite without prompting \n");\r
+    DISPLAY( " -H     : Help (this text + advanced options)\n");\r
+    return 0;\r
+}\r
+\r
+int usage_advanced()\r
+{\r
+    DISPLAY( "\nAdvanced options :\n");\r
+    DISPLAY( " -t     : test compressed file \n");\r
+    DISPLAY( " -B#    : Block size [4-7](default : 7)\n");\r
+    DISPLAY( " -BD    : Block dependency (improve compression ratio)\n");\r
+    DISPLAY( " -BX    : enable block checksum (default:disabled)\n");\r
+    DISPLAY( " -Sx    : disable stream checksum (default:enabled)\n");\r
+    DISPLAY( " -b#    : benchmark files, using # [0-1] compression level\n");\r
+    DISPLAY( " -i#    : iteration loops [1-9](default : 3), benchmark mode only\n");\r
+    DISPLAY( "input   : can be 'stdin' (pipe) or a filename\n");\r
+    DISPLAY( "output  : can be 'stdout'(pipe) or a filename or 'null'\n");\r
+    DISPLAY( "          example 1 : lz4c -hc stdin compressedfile.lz4\n");\r
+    DISPLAY( "          example 2 : lz4c -hcyB4D filename \n");\r
+    return 0;\r
+}\r
+\r
+int badusage(char* exename)\r
+{\r
+    DISPLAY("Wrong parameters\n");\r
+    usage(exename);\r
+    return 0;\r
+}\r
+\r
+\r
+static int          LZ4S_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); }\r
+static unsigned int LZ4S_GetCheckBits_FromXXH (unsigned int xxh) { return (xxh >> 8) & _8BITS; }\r
+static int          LZ4S_isSkippableMagicNumber(unsigned int magic) { return (magic & LZ4S_SKIPPABLEMASK) == LZ4S_SKIPPABLE0; }\r
+\r
+\r
+int get_fileHandle(char* input_filename, char* output_filename, FILE** pfinput, FILE** pfoutput)\r
+{\r
+\r
+    if (!strcmp (input_filename, stdinmark)) \r
+    {\r
+        DISPLAY( "Using stdin for input\n");\r
+        *pfinput = stdin;\r
+#ifdef _WIN32 // Need to set stdin/stdout to binary mode specifically for windows\r
+        _setmode( _fileno( stdin ), _O_BINARY );\r
+#endif\r
+    } \r
+    else \r
+    {\r
+        *pfinput = fopen(input_filename, "rb");\r
+    }\r
+\r
+    if (!strcmp (output_filename, stdoutmark)) \r
+    {\r
+        DISPLAY( "Using stdout for output\n");\r
+        *pfoutput = stdout;\r
+#ifdef _WIN32 // Need to set stdin/stdout to binary mode specifically for windows\r
+        _setmode( _fileno( stdout ), _O_BINARY );\r
+#endif\r
+    } \r
+    else \r
+    {\r
+        // Check if destination file already exists\r
+        *pfoutput=0;\r
+        if (output_filename != nulmark) *pfoutput = fopen( output_filename, "rb" );\r
+        if (*pfoutput!=0) \r
+        { \r
+            char ch;\r
+            fclose(*pfoutput); \r
+            DISPLAY( "Warning : %s already exists\n", output_filename); \r
+            if (!overwrite)\r
+            {\r
+                DISPLAY( "Overwrite ? (Y/N) : ");\r
+                ch = (char)getchar();\r
+                if (ch!='Y') EXM_THROW(11, "Operation aborted : %s already exists", output_filename);\r
+            }\r
+        }\r
+        *pfoutput = fopen( output_filename, "wb" );\r
+    }\r
+\r
+    if ( *pfinput==0 ) EXM_THROW(12, "Pb opening %s", input_filename);\r
+    if ( *pfoutput==0) EXM_THROW(13, "Pb opening %s", output_filename); \r
+\r
+    return 0;\r
+}\r
+\r
+\r
+\r
+int legacy_compress_file(char* input_filename, char* output_filename, int compressionlevel)\r
+{\r
+    int (*compressionFunction)(const char*, char*, int);\r
+    unsigned long long filesize = 0;\r
+    unsigned long long compressedfilesize = MAGICNUMBER_SIZE;\r
+    char* in_buff;\r
+    char* out_buff;\r
+    FILE* finput;\r
+    FILE* foutput;\r
+    int displayLevel = (compressionlevel>0);\r
+    clock_t start, end;\r
+    size_t sizeCheck;\r
+\r
+\r
+    // Init\r
+    switch (compressionlevel)\r
+    {\r
+    case 0 : compressionFunction = LZ4_compress; break;\r
+    case 1 : compressionFunction = LZ4_compressHC; break;\r
+    default: compressionFunction = LZ4_compress;\r
+    }\r
+    start = clock();\r
+    get_fileHandle(input_filename, output_filename, &finput, &foutput);\r
+\r
+    // Allocate Memory\r
+    in_buff = (char*)malloc(LEGACY_BLOCKSIZE);\r
+    out_buff = (char*)malloc(LZ4_compressBound(LEGACY_BLOCKSIZE));\r
+    if (!in_buff || !out_buff) EXM_THROW(21, "Allocation error : not enough memory");\r
+\r
+    // Write Archive Header\r
+    *(unsigned int*)out_buff = LITTLE_ENDIAN_32(LEGACY_MAGICNUMBER);\r
+    sizeCheck = fwrite(out_buff, 1, MAGICNUMBER_SIZE, foutput);\r
+    if (sizeCheck!=MAGICNUMBER_SIZE) EXM_THROW(22, "Write error : cannot write header");\r
+\r
+    // Main Loop\r
+    while (1)\r
+    {\r
+        unsigned int outSize;\r
+        // Read Block\r
+        int inSize = (int) fread(in_buff, (size_t)1, (size_t)LEGACY_BLOCKSIZE, finput);\r
+        if( inSize<=0 ) break;\r
+        filesize += inSize;\r
+        if (displayLevel) DISPLAY("Read : %i MB  \r", (int)(filesize>>20));\r
+\r
+        // Compress Block\r
+        outSize = compressionFunction(in_buff, out_buff+4, inSize);\r
+        compressedfilesize += outSize+4;\r
+        if (displayLevel) DISPLAY("Read : %i MB  ==> %.2f%%\r", (int)(filesize>>20), (double)compressedfilesize/filesize*100);\r
+\r
+        // Write Block\r
+        * (unsigned int*) out_buff = LITTLE_ENDIAN_32(outSize);\r
+        sizeCheck = fwrite(out_buff, 1, outSize+4, foutput);\r
+        if (sizeCheck!=(size_t)(outSize+4)) EXM_THROW(23, "Write error : cannot write compressed block");\r
+    }\r
+\r
+    // Status\r
+    end = clock();\r
+    DISPLAY( "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",\r
+        (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);\r
+    {\r
+        double seconds = (double)(end - start)/CLOCKS_PER_SEC;\r
+        DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);\r
+    }\r
+\r
+    // Close & Free\r
+    free(in_buff);\r
+    free(out_buff);\r
+    fclose(finput);\r
+    fclose(foutput);\r
+\r
+    return 0;\r
+}\r
+\r
+\r
+int compress_file_blockDependency(char* input_filename, char* output_filename, int compressionlevel)\r
+{\r
+    void* (*initFunction)       (const char*);\r
+    int   (*compressionFunction)(void*, const char*, char*, int, int);\r
+    char* (*translateFunction)  (void*);\r
+    int   (*freeFunction)       (void*);\r
+    void* ctx;\r
+    unsigned long long filesize = 0;\r
+    unsigned long long compressedfilesize = 0;\r
+    unsigned int checkbits;\r
+    char* in_buff, *in_start, *in_end;\r
+    char* out_buff;\r
+    FILE* finput;\r
+    FILE* foutput;\r
+    int errorcode;\r
+    int displayLevel = (compressionlevel>0);\r
+    clock_t start, end;\r
+    unsigned int blockSize, inputBufferSize;\r
+    size_t sizeCheck, header_size;\r
+    void* streamChecksumState=NULL;\r
+\r
+\r
+    // Init\r
+    start = clock();\r
+    switch (compressionlevel)\r
+    {\r
+    case 0 :\r
+    case 1 :\r
+    default:\r
+        initFunction = LZ4_createHC;\r
+        compressionFunction = LZ4_compressHC_limitedOutput_continue;\r
+        translateFunction = LZ4_slideInputBufferHC;\r
+        freeFunction = LZ4_freeHC;\r
+    }\r
+    errorcode = get_fileHandle(input_filename, output_filename, &finput, &foutput);\r
+    if (errorcode) return errorcode;\r
+    blockSize = LZ4S_GetBlockSize_FromBlockId (blockSizeId);\r
+\r
+    // Allocate Memory\r
+    inputBufferSize = blockSize + 64 KB;\r
+    if (inputBufferSize < MIN_STREAM_BUFSIZE) inputBufferSize = MIN_STREAM_BUFSIZE;\r
+    in_buff  = (char*)malloc(inputBufferSize);\r
+    out_buff = (char*)malloc(blockSize+CACHELINE);\r
+    if (!in_buff || !out_buff) EXM_THROW(31, "Allocation error : not enough memory");\r
+    in_start = in_buff; in_end = in_buff + inputBufferSize;\r
+    if (streamChecksum) streamChecksumState = XXH32_init(LZ4S_CHECKSUM_SEED);\r
+    ctx = initFunction(in_buff);\r
+\r
+    // Write Archive Header\r
+    *(unsigned int*)out_buff = LITTLE_ENDIAN_32(LZ4S_MAGICNUMBER);   // Magic Number, in Little Endian convention\r
+    *(out_buff+4)  = (1 & _2BITS) << 6 ;                             // Version('01')\r
+    *(out_buff+4) |= (blockIndependence & _1BIT) << 5;\r
+    *(out_buff+4) |= (blockChecksum & _1BIT) << 4;\r
+    *(out_buff+4) |= (streamChecksum & _1BIT) << 2;\r
+    *(out_buff+5)  = (char)((blockSizeId & _3BITS) << 4);\r
+    checkbits = XXH32((out_buff+4), 2, LZ4S_CHECKSUM_SEED);\r
+    checkbits = LZ4S_GetCheckBits_FromXXH(checkbits);\r
+    *(out_buff+6)  = (unsigned char) checkbits;\r
+    header_size = 7;\r
+    sizeCheck = fwrite(out_buff, 1, header_size, foutput);\r
+    if (sizeCheck!=header_size) EXM_THROW(32, "Write error : cannot write header");\r
+    compressedfilesize += header_size;\r
+\r
+    // Main Loop\r
+    while (1)\r
+    {\r
+        unsigned int outSize;\r
+        unsigned int inSize;\r
+        // Read Block\r
+        if ((in_start+blockSize) > in_end) in_start = translateFunction(ctx);\r
+        inSize = (unsigned int) fread(in_start, (size_t)1, (size_t)blockSize, finput);\r
+        if( inSize<=0 ) break;   // No more input : end of compression\r
+        filesize += inSize;\r
+        if (displayLevel) DISPLAY("Read : %i MB  \r", (int)(filesize>>20));\r
+        if (streamChecksum) XXH32_update(streamChecksumState, in_start, inSize);\r
+\r
+        // Compress Block\r
+        outSize = compressionFunction(ctx, in_start, out_buff+4, inSize, inSize-1);\r
+        if (outSize > 0) compressedfilesize += outSize+4; else compressedfilesize += inSize+4;\r
+        if (blockChecksum) compressedfilesize+=4;\r
+        if (displayLevel) DISPLAY("Read : %i MB  ==> %.2f%%\r", (int)(filesize>>20), (double)compressedfilesize/filesize*100);\r
+\r
+        // Write Block\r
+        if (outSize > 0)\r
+        {\r
+            unsigned int checksum;\r
+            int sizeToWrite;\r
+            * (unsigned int*) out_buff = LITTLE_ENDIAN_32(outSize);\r
+            if (blockChecksum)\r
+            {\r
+                checksum = XXH32(out_buff+4, outSize, LZ4S_CHECKSUM_SEED);\r
+                * (unsigned int*) (out_buff+4+outSize) = LITTLE_ENDIAN_32(checksum);\r
+            }\r
+            sizeToWrite = 4 + outSize + (4*blockChecksum);\r
+            sizeCheck = fwrite(out_buff, 1, sizeToWrite, foutput);\r
+            if (sizeCheck!=(size_t)(sizeToWrite)) EXM_THROW(33, "Write error : cannot write compressed block");\r
+\r
+        }\r
+        else   // Copy Original\r
+        {\r
+            unsigned int checksum;\r
+            * (unsigned int*) out_buff = LITTLE_ENDIAN_32(inSize|0x80000000);   // Add Uncompressed flag\r
+            sizeCheck = fwrite(out_buff, 1, 4, foutput);\r
+            if (sizeCheck!=(size_t)(4)) EXM_THROW(34, "Write error : cannot write block header");\r
+            sizeCheck = fwrite(in_start, 1, inSize, foutput);\r
+            if (sizeCheck!=(size_t)(inSize)) EXM_THROW(35, "Write error : cannot write block");\r
+            if (blockChecksum)\r
+            {\r
+                checksum = XXH32(in_start, inSize, LZ4S_CHECKSUM_SEED);\r
+                * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);\r
+                sizeCheck = fwrite(out_buff, 1, 4, foutput);\r
+                if (sizeCheck!=(size_t)(4)) EXM_THROW(36, "Write error : cannot write block checksum");\r
+            }\r
+        }\r
+        in_start += inSize;\r
+    }\r
+\r
+    // End of Stream mark\r
+    * (unsigned int*) out_buff = LZ4S_EOS;\r
+    sizeCheck = fwrite(out_buff, 1, 4, foutput);\r
+    if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write end of stream");\r
+    compressedfilesize += 4;\r
+    if (streamChecksum)\r
+    {\r
+        unsigned int checksum = XXH32_digest(streamChecksumState);\r
+        * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);\r
+        sizeCheck = fwrite(out_buff, 1, 4, foutput);\r
+        if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write stream checksum");\r
+        compressedfilesize += 4;\r
+    }\r
+\r
+    // Status\r
+    end = clock();\r
+    DISPLAY( "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",\r
+        (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);\r
+    {\r
+        double seconds = (double)(end - start)/CLOCKS_PER_SEC;\r
+        DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);\r
+    }\r
+\r
+    // Close & Free\r
+    freeFunction(ctx);\r
+    free(in_buff);\r
+    free(out_buff);\r
+    fclose(finput);\r
+    fclose(foutput);\r
+\r
+    return 0;\r
+}\r
+\r
+\r
+int compress_file(char* input_filename, char* output_filename, int compressionlevel)\r
+{\r
+    int (*compressionFunction)(const char*, char*, int, int);\r
+    unsigned long long filesize = 0;\r
+    unsigned long long compressedfilesize = 0;\r
+    unsigned int checkbits;\r
+    char* in_buff;\r
+    char* out_buff;\r
+    FILE* finput;\r
+    FILE* foutput;\r
+    int errorcode;\r
+    int displayLevel = (compressionlevel>0);\r
+    clock_t start, end;\r
+    int blockSize;\r
+    size_t sizeCheck, header_size;\r
+    void* streamChecksumState=NULL;\r
+\r
+    // Branch out\r
+    if (blockIndependence==0) return compress_file_blockDependency(input_filename, output_filename, compressionlevel);\r
+\r
+    // Init\r
+    start = clock();\r
+    switch (compressionlevel)\r
+    {\r
+    case 0 : compressionFunction = LZ4_compress_limitedOutput; break;\r
+    case 1 : compressionFunction = LZ4_compressHC_limitedOutput; break;\r
+    default: compressionFunction = LZ4_compress_limitedOutput;\r
+    }\r
+    errorcode = get_fileHandle(input_filename, output_filename, &finput, &foutput);\r
+    if (errorcode) return errorcode;\r
+    blockSize = LZ4S_GetBlockSize_FromBlockId (blockSizeId);\r
+\r
+    // Allocate Memory\r
+    in_buff  = (char*)malloc(blockSize);\r
+    out_buff = (char*)malloc(blockSize+CACHELINE);\r
+    if (!in_buff || !out_buff) EXM_THROW(31, "Allocation error : not enough memory");\r
+    if (streamChecksum) streamChecksumState = XXH32_init(LZ4S_CHECKSUM_SEED);\r
+\r
+    // Write Archive Header\r
+    *(unsigned int*)out_buff = LITTLE_ENDIAN_32(LZ4S_MAGICNUMBER);   // Magic Number, in Little Endian convention\r
+    *(out_buff+4)  = (1 & _2BITS) << 6 ;                             // Version('01')\r
+    *(out_buff+4) |= (blockIndependence & _1BIT) << 5;\r
+    *(out_buff+4) |= (blockChecksum & _1BIT) << 4;\r
+    *(out_buff+4) |= (streamChecksum & _1BIT) << 2;\r
+    *(out_buff+5)  = (char)((blockSizeId & _3BITS) << 4);\r
+    checkbits = XXH32((out_buff+4), 2, LZ4S_CHECKSUM_SEED);\r
+    checkbits = LZ4S_GetCheckBits_FromXXH(checkbits);\r
+    *(out_buff+6)  = (unsigned char) checkbits;\r
+    header_size = 7;\r
+    sizeCheck = fwrite(out_buff, 1, header_size, foutput);\r
+    if (sizeCheck!=header_size) EXM_THROW(32, "Write error : cannot write header");\r
+    compressedfilesize += header_size;\r
+\r
+    // Main Loop\r
+    while (1)\r
+    {\r
+        unsigned int outSize;\r
+        // Read Block\r
+        unsigned int inSize = (unsigned int) fread(in_buff, (size_t)1, (size_t)blockSize, finput);\r
+        if( inSize<=0 ) break;   // No more input : end of compression\r
+        filesize += inSize;\r
+        if (displayLevel) DISPLAY("Read : %i MB  \r", (int)(filesize>>20));\r
+        if (streamChecksum) XXH32_update(streamChecksumState, in_buff, inSize);\r
+\r
+        // Compress Block\r
+        outSize = compressionFunction(in_buff, out_buff+4, inSize, inSize-1);\r
+        if (outSize > 0) compressedfilesize += outSize+4; else compressedfilesize += inSize+4;\r
+        if (blockChecksum) compressedfilesize+=4;\r
+        if (displayLevel) DISPLAY("Read : %i MB  ==> %.2f%%\r", (int)(filesize>>20), (double)compressedfilesize/filesize*100);\r
+\r
+        // Write Block\r
+        if (outSize > 0)\r
+        {\r
+            unsigned int checksum;\r
+            int sizeToWrite;\r
+            * (unsigned int*) out_buff = LITTLE_ENDIAN_32(outSize);\r
+            if (blockChecksum)\r
+            {\r
+                checksum = XXH32(out_buff+4, outSize, LZ4S_CHECKSUM_SEED);\r
+                * (unsigned int*) (out_buff+4+outSize) = LITTLE_ENDIAN_32(checksum);\r
+            }\r
+            sizeToWrite = 4 + outSize + (4*blockChecksum);\r
+            sizeCheck = fwrite(out_buff, 1, sizeToWrite, foutput);\r
+            if (sizeCheck!=(size_t)(sizeToWrite)) EXM_THROW(33, "Write error : cannot write compressed block");\r
+\r
+        }\r
+        else  // Copy Original\r
+        {\r
+            unsigned int checksum;\r
+            * (unsigned int*) out_buff = LITTLE_ENDIAN_32(inSize|0x80000000);   // Add Uncompressed flag\r
+            sizeCheck = fwrite(out_buff, 1, 4, foutput);\r
+            if (sizeCheck!=(size_t)(4)) EXM_THROW(34, "Write error : cannot write block header");\r
+            sizeCheck = fwrite(in_buff, 1, inSize, foutput);\r
+            if (sizeCheck!=(size_t)(inSize)) EXM_THROW(35, "Write error : cannot write block");\r
+            if (blockChecksum)\r
+            {\r
+                checksum = XXH32(in_buff, inSize, LZ4S_CHECKSUM_SEED);\r
+                * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);\r
+                sizeCheck = fwrite(out_buff, 1, 4, foutput);\r
+                if (sizeCheck!=(size_t)(4)) EXM_THROW(36, "Write error : cannot write block checksum");\r
+            }\r
+        }\r
+    }\r
+\r
+    // End of Stream mark\r
+    * (unsigned int*) out_buff = LZ4S_EOS;\r
+    sizeCheck = fwrite(out_buff, 1, 4, foutput);\r
+    if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write end of stream");\r
+    compressedfilesize += 4;\r
+    if (streamChecksum)\r
+    {\r
+        unsigned int checksum = XXH32_digest(streamChecksumState);\r
+        * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);\r
+        sizeCheck = fwrite(out_buff, 1, 4, foutput);\r
+        if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write stream checksum");\r
+        compressedfilesize += 4;\r
+    }\r
+\r
+    // Status\r
+    end = clock();\r
+    DISPLAY( "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",\r
+        (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);\r
+    {\r
+        double seconds = (double)(end - start)/CLOCKS_PER_SEC;\r
+        DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);\r
+    }\r
+\r
+    // Close & Free\r
+    free(in_buff);\r
+    free(out_buff);\r
+    fclose(finput);\r
+    fclose(foutput);\r
+\r
+    return 0;\r
+}\r
+\r
+\r
+unsigned long long decodeLegacyStream(FILE* finput, FILE* foutput)\r
+{\r
+    unsigned long long filesize = 0;\r
+    char* in_buff;\r
+    char* out_buff;\r
+    size_t uselessRet;\r
+    int sinkint;\r
+    unsigned int blockSize;\r
+    size_t sizeCheck;\r
+\r
+\r
+    // Allocate Memory\r
+    in_buff = (char*)malloc(LZ4_compressBound(LEGACY_BLOCKSIZE));\r
+    out_buff = (char*)malloc(LEGACY_BLOCKSIZE);\r
+    if (!in_buff || !out_buff) EXM_THROW(51, "Allocation error : not enough memory");\r
+\r
+    // Main Loop\r
+    while (1)\r
+    {\r
+        // Block Size\r
+        uselessRet = fread(&blockSize, 1, 4, finput);\r
+        if( uselessRet==0 ) break;                 // Nothing to read : file read is completed\r
+        blockSize = LITTLE_ENDIAN_32(blockSize);   // Convert to Little Endian\r
+        if (blockSize > LZ4_COMPRESSBOUND(LEGACY_BLOCKSIZE)) \r
+        {   // Cannot read next block : maybe new stream ?\r
+            fseek(finput, -4, SEEK_CUR);\r
+            break;\r
+        }\r
+\r
+        // Read Block\r
+        uselessRet = fread(in_buff, 1, blockSize, finput);\r
+\r
+        // Decode Block\r
+        sinkint = LZ4_decompress_safe(in_buff, out_buff, blockSize, LEGACY_BLOCKSIZE);\r
+        if (sinkint < 0) EXM_THROW(52, "Decoding Failed ! Corrupted input detected !");\r
+        filesize += sinkint;\r
+\r
+        // Write Block\r
+        sizeCheck = fwrite(out_buff, 1, sinkint, foutput);\r
+        if (sizeCheck != (size_t)sinkint) EXM_THROW(53, "Write error : cannot write decoded block into output\n");\r
+    }\r
+\r
+    // Free\r
+    free(in_buff);\r
+    free(out_buff);\r
+\r
+    return filesize;\r
+}\r
+\r
+\r
+unsigned long long decodeLZ4S(FILE* finput, FILE* foutput)\r
+{\r
+    unsigned long long filesize = 0;\r
+    char* in_buff;\r
+    char* out_buff, *out_start, *out_end;\r
+    unsigned char descriptor[LZ4S_MAXHEADERSIZE];\r
+    size_t nbReadBytes;\r
+    int decodedBytes=0;\r
+    unsigned int maxBlockSize;\r
+    size_t sizeCheck;\r
+    int blockChecksumFlag, streamChecksumFlag, blockIndependenceFlag;\r
+    void* streamChecksumState=NULL;\r
+    int (*decompressionFunction)(const char*, char*, int, int) = LZ4_decompress_safe;\r
+    unsigned int prefix64k = 0;\r
+\r
+    // Decode stream descriptor\r
+    nbReadBytes = fread(descriptor, 1, 3, finput);\r
+    if (nbReadBytes != 3) EXM_THROW(61, "Unreadable header");\r
+    {\r
+        int version       = (descriptor[0] >> 6) & _2BITS;\r
+        int streamSize    = (descriptor[0] >> 3) & _1BIT;\r
+        int reserved1     = (descriptor[0] >> 1) & _1BIT;\r
+        int dictionary    = (descriptor[0] >> 0) & _1BIT;\r
+\r
+        int reserved2     = (descriptor[1] >> 7) & _1BIT;\r
+        int blockSizeId   = (descriptor[1] >> 4) & _3BITS;\r
+        int reserved3     = (descriptor[1] >> 0) & _4BITS;\r
+        int checkBits     = (descriptor[2] >> 0) & _8BITS;\r
+        int checkBits_xxh32;\r
+\r
+        blockIndependenceFlag=(descriptor[0] >> 5) & _1BIT;\r
+        blockChecksumFlag = (descriptor[0] >> 4) & _1BIT;\r
+        streamChecksumFlag= (descriptor[0] >> 2) & _1BIT;\r
+\r
+        if (version != 1)       EXM_THROW(62, "Wrong version number");\r
+        if (streamSize == 1)    EXM_THROW(64, "Does not support stream size");  \r
+        if (reserved1 != 0)     EXM_THROW(65, "Wrong value for reserved bits");\r
+        if (dictionary == 1)    EXM_THROW(66, "Does not support dictionary"); \r
+        if (reserved2 != 0)     EXM_THROW(67, "Wrong value for reserved bits");\r
+        if (blockSizeId < 4)    EXM_THROW(68, "Unsupported block size"); \r
+        if (reserved3 != 0)     EXM_THROW(67, "Wrong value for reserved bits");\r
+        maxBlockSize = LZ4S_GetBlockSize_FromBlockId(blockSizeId);\r
+        // Checkbits verification\r
+        descriptor[1] &= 0xF0;\r
+        checkBits_xxh32 = XXH32(descriptor, 2, LZ4S_CHECKSUM_SEED);\r
+        checkBits_xxh32 = LZ4S_GetCheckBits_FromXXH(checkBits_xxh32);\r
+        if (checkBits != checkBits_xxh32) EXM_THROW(69, "Stream descriptor error detected");\r
+    }\r
+\r
+    if (!blockIndependenceFlag)\r
+    {\r
+        decompressionFunction = LZ4_decompress_safe_withPrefix64k;\r
+        prefix64k = 64 KB;\r
+    }\r
+\r
+    // Allocate Memory\r
+    {\r
+        unsigned int outbuffSize = prefix64k+maxBlockSize;\r
+        in_buff  = (char*)malloc(maxBlockSize);\r
+        if (outbuffSize < MIN_STREAM_BUFSIZE) outbuffSize = MIN_STREAM_BUFSIZE;\r
+        out_buff = (char*)malloc(outbuffSize); \r
+        out_end = out_buff + outbuffSize;\r
+        out_start = out_buff + prefix64k;\r
+        if (!in_buff || !out_buff) EXM_THROW(70, "Allocation error : not enough memory");\r
+    }\r
+    if (streamChecksumFlag) streamChecksumState = XXH32_init(LZ4S_CHECKSUM_SEED);\r
+\r
+    // Main Loop\r
+    while (1)\r
+    {\r
+        unsigned int blockSize, uncompressedFlag;\r
+\r
+        // Block Size\r
+        nbReadBytes = fread(&blockSize, 1, 4, finput);\r
+        if( nbReadBytes != 4 ) EXM_THROW(71, "Read error : cannot read next block size");\r
+        if (blockSize == LZ4S_EOS) break;          // End of Stream Mark : stream is completed\r
+        blockSize = LITTLE_ENDIAN_32(blockSize);   // Convert to little endian\r
+        uncompressedFlag = blockSize >> 31;\r
+        blockSize &= 0x7FFFFFFF;\r
+        if (blockSize > maxBlockSize) EXM_THROW(72, "Error : invalid block size");\r
+\r
+        // Read Block\r
+        nbReadBytes = fread(in_buff, 1, blockSize, finput);\r
+        if( nbReadBytes != blockSize ) EXM_THROW(73, "Read error : cannot read data block" );\r
+\r
+        // Check Block\r
+        if (blockChecksumFlag)\r
+        {\r
+            unsigned int checksum = XXH32(in_buff, blockSize, LZ4S_CHECKSUM_SEED);\r
+            unsigned int readChecksum;\r
+            sizeCheck = fread(&readChecksum, 1, 4, finput);\r
+            if( sizeCheck != 4 ) EXM_THROW(74, "Read error : cannot read next block size");\r
+            readChecksum = LITTLE_ENDIAN_32(readChecksum);   // Convert to little endian\r
+            if (checksum != readChecksum) EXM_THROW(75, "Error : invalid block checksum detected");\r
+        }\r
+\r
+        if (uncompressedFlag)\r
+        {\r
+            // Write uncompressed Block\r
+            sizeCheck = fwrite(in_buff, 1, blockSize, foutput);\r
+            if (sizeCheck != (size_t)blockSize) EXM_THROW(76, "Write error : cannot write data block");\r
+            filesize += blockSize;\r
+            if (streamChecksumFlag) XXH32_update(streamChecksumState, in_buff, blockSize);\r
+            if (!blockIndependenceFlag)\r
+            {\r
+                if (blockSize >= prefix64k)\r
+                {\r
+                    memcpy(out_buff, in_buff + (blockSize - prefix64k), prefix64k);   // Required for reference for next blocks\r
+                    out_start = out_buff + prefix64k;\r
+                    continue;\r
+                }\r
+                else\r
+                {\r
+                    memcpy(out_start, in_buff, blockSize);\r
+                }\r
+            }\r
+        }\r
+        else\r
+        {\r
+            // Decode Block\r
+            decodedBytes = decompressionFunction(in_buff, out_start, blockSize, maxBlockSize);\r
+            if (decodedBytes < 0) EXM_THROW(77, "Decoding Failed ! Corrupted input detected !");\r
+            filesize += decodedBytes;\r
+            if (streamChecksumFlag) XXH32_update(streamChecksumState, out_start, decodedBytes);\r
+\r
+            // Write Block\r
+            sizeCheck = fwrite(out_start, 1, decodedBytes, foutput);\r
+            if (sizeCheck != (size_t)decodedBytes) EXM_THROW(78, "Write error : cannot write decoded block\n");\r
+        }\r
+\r
+        if (!blockIndependenceFlag)\r
+        {\r
+            out_start += decodedBytes;\r
+            if (out_start + maxBlockSize > out_end) \r
+            {\r
+                memcpy(out_buff, out_start - prefix64k, prefix64k); \r
+                out_start = out_buff + prefix64k; \r
+            }\r
+        }\r
+    }\r
+\r
+    // Stream Checksum\r
+    if (streamChecksumFlag)\r
+    {\r
+        unsigned int checksum = XXH32_digest(streamChecksumState);\r
+        unsigned int readChecksum;\r
+        sizeCheck = fread(&readChecksum, 1, 4, finput);\r
+        if (sizeCheck != 4) EXM_THROW(74, "Read error : cannot read stream checksum");\r
+        readChecksum = LITTLE_ENDIAN_32(readChecksum);   // Convert to little endian\r
+        if (checksum != readChecksum) EXM_THROW(75, "Error : invalid stream checksum detected");\r
+    }\r
+\r
+    // Free\r
+    free(in_buff);\r
+    free(out_buff);\r
+\r
+    return filesize;\r
+}\r
+\r
+\r
+unsigned long long selectDecoder( FILE* finput,  FILE* foutput)\r
+{\r
+    unsigned int magicNumber, size;\r
+    int errorNb;\r
+    size_t nbReadBytes;\r
+\r
+    // Check Archive Header\r
+    nbReadBytes = fread(&magicNumber, 1, MAGICNUMBER_SIZE, finput);\r
+    if (nbReadBytes==0) return 0;                  // EOF\r
+    if (nbReadBytes != MAGICNUMBER_SIZE) EXM_THROW(41, "Unrecognized header : Magic Number unreadable");\r
+    magicNumber = LITTLE_ENDIAN_32(magicNumber);   // Convert to Little Endian format\r
+    if (LZ4S_isSkippableMagicNumber(magicNumber)) magicNumber = LZ4S_SKIPPABLE0;  // fold skippable magic numbers\r
+\r
+    switch(magicNumber)\r
+    {\r
+    case LZ4S_MAGICNUMBER:\r
+        return decodeLZ4S(finput, foutput);\r
+    case LEGACY_MAGICNUMBER:\r
+        DISPLAY("Detected : Legacy format \n");\r
+        return decodeLegacyStream(finput, foutput);\r
+    case LZ4S_SKIPPABLE0:\r
+        nbReadBytes = fread(&size, 1, 4, finput);\r
+        if (nbReadBytes != 4) EXM_THROW(42, "Stream error : skippable size unreadable");\r
+        size = LITTLE_ENDIAN_32(size);     // Convert to Little Endian format\r
+        errorNb = fseek(finput, size, SEEK_CUR);\r
+        if (errorNb != 0) EXM_THROW(43, "Stream error : cannot skip skippable area");\r
+        return selectDecoder(finput, foutput);\r
+    default:\r
+        if (ftell(finput) == MAGICNUMBER_SIZE) EXM_THROW(44,"Unrecognized header : file cannot be decoded");   // Wrong magic number at the beginning of 1st stream\r
+        DISPLAY("Stream followed by unrecognized data\n");\r
+        return 0;\r
+    }\r
+}\r
+\r
+\r
+int decodeFile(char* input_filename, char* output_filename)\r
+{\r
+    unsigned long long filesize = 0, decodedSize=0;\r
+    FILE* finput;\r
+    FILE* foutput;\r
+    clock_t start, end;\r
+\r
+\r
+    // Init\r
+    start = clock();\r
+    get_fileHandle(input_filename, output_filename, &finput, &foutput);\r
+\r
+    // Loop over multiple streams\r
+    do \r
+    {\r
+        decodedSize = selectDecoder(finput, foutput);\r
+        filesize += decodedSize;\r
+    } while (decodedSize);\r
+\r
+    // Final Status\r
+    end = clock();\r
+    DISPLAY( "Successfully decoded %llu bytes \n", filesize);\r
+    {\r
+        double seconds = (double)(end - start)/CLOCKS_PER_SEC;\r
+        DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);\r
+    }\r
+\r
+    // Close\r
+    fclose(finput);\r
+    fclose(foutput);\r
+\r
+    // Error status = OK\r
+    return 0;\r
+}\r
+\r
+\r
+int main(int argc, char** argv)\r
+{\r
+    int i,\r
+        cLevel=0,\r
+        decode=0,\r
+        bench=0,\r
+        filenamesStart=2,\r
+        legacy_format=0;\r
+    char* exename=argv[0];\r
+    char* input_filename=0;\r
+    char* output_filename=0;\r
+    char nullinput[] = NULL_INPUT;\r
+    char extension[] = EXTENSION;\r
+\r
+    // Welcome message\r
+    DISPLAY( WELCOME_MESSAGE);\r
+\r
+    if (argc<2) { badusage(exename); return 1; }\r
+\r
+    for(i=1; i<argc; i++)\r
+    {\r
+        char* argument = argv[i];\r
+\r
+        if(!argument) continue;   // Protection if argument empty\r
+\r
+        // Decode command (note : aggregated commands are allowed)\r
+        if (argument[0]=='-')\r
+        {\r
+            while (argument[1]!=0)\r
+            {\r
+                argument ++;\r
+\r
+                switch(argument[0])\r
+                {\r
+                    // Display help on usage\r
+                case 'H': usage(exename); usage_advanced(); return 0;\r
+\r
+                    // Compression (default)\r
+                case 'c': if ((argument[1] >='0') && (argument[1] <='1')) { cLevel=argument[1] - '0'; argument++; } break;\r
+                case 'h': if (argument[1]=='c') { cLevel=1; argument++; } break;\r
+\r
+                    // Use Legacy format (hidden option)\r
+                case 'l': legacy_format=1; break;\r
+\r
+                    // Decoding\r
+                case 'd': decode=1; break;\r
+\r
+                    // Test\r
+                case 't': decode=1; output_filename=nulmark; break;\r
+\r
+                    // Modify Block Properties\r
+                case 'B':\r
+                    while (argument[1]!=0)\r
+                    switch(argument[1])\r
+                    {\r
+                    case '4':\r
+                    case '5':\r
+                    case '6':\r
+                    case '7':\r
+                    { \r
+                        int B = argument[1] - '0'; \r
+                        int S = 1 << (8 + 2*B); \r
+                        BMK_SetBlocksize(S); \r
+                        blockSizeId = B;\r
+                        argument++;\r
+                        break;\r
+                    }\r
+                    case 'D': blockIndependence = 0, argument++; break;\r
+                    case 'X': blockChecksum = 1, argument ++; break;\r
+                    default : goto _exit_blockProperties;\r
+                    }\r
+_exit_blockProperties:\r
+                    break;\r
+\r
+                    // Modify Stream properties\r
+                case 'S': if (argument[1]=='x') { streamChecksum=0; argument++; break; } else { badusage(exename); return 1; }\r
+\r
+                    // Bench\r
+                case 'b': bench=1; \r
+                    if ((argument[1] >='0') && (argument[1] <='1')) { cLevel=argument[1] - '0'; argument++; } \r
+                    break;\r
+\r
+                    // Modify Nb Iterations (benchmark only)\r
+                case 'i': \r
+                    if ((argument[1] >='1') && (argument[1] <='9'))\r
+                    {\r
+                        int iters = argument[1] - '0'; \r
+                        BMK_SetNbIterations(iters); \r
+                        argument++;\r
+                    }\r
+                    break;\r
+\r
+                    // Pause at the end (benchmark only) (hidden option)\r
+                case 'p': BMK_SetPause(); break;\r
+\r
+                    // Overwrite\r
+                case 'y': overwrite=1; break;\r
+\r
+                    // Unrecognised command\r
+                default : badusage(exename); return 1;\r
+                }\r
+            }\r
+            continue;\r
+        }\r
+\r
+        // first provided filename is input\r
+        if (!input_filename) { input_filename=argument; filenamesStart=i; continue; }\r
+\r
+        // second provided filename is output\r
+        if (!output_filename)\r
+        {\r
+            output_filename=argument;\r
+            if (!strcmp (output_filename, nullinput)) output_filename = nulmark;\r
+            continue;\r
+        }\r
+    }\r
+\r
+    // No input filename ==> Error\r
+    if(!input_filename) { badusage(exename); return 1; }\r
+\r
+    if (bench) return BMK_benchFile(argv+filenamesStart, argc-filenamesStart, cLevel);\r
+\r
+    // No output filename ==> build one automatically (when possible)\r
+    if (!output_filename) \r
+    { \r
+        if (!decode)   // compression\r
+        {\r
+            int i=0, l=0;\r
+            while (input_filename[l]!=0) l++;\r
+            output_filename = (char*)calloc(1,l+5);\r
+            for (i=0;i<l;i++) output_filename[i] = input_filename[i];\r
+            for (i=l;i<l+4;i++) output_filename[i] = extension[i-l];\r
+        }\r
+        else           // decompression (input file must respect format extension ".lz4")\r
+        {\r
+            int inl=0,outl;\r
+            while (input_filename[inl]!=0) inl++;\r
+            output_filename = (char*)calloc(1,inl+1);\r
+            for (outl=0;outl<inl;outl++) output_filename[outl] = input_filename[outl];\r
+            if (inl>4)\r
+                while ((outl >= inl-4) && (input_filename[outl] ==  extension[outl-inl+4])) output_filename[outl--]=0;\r
+            if (outl != inl-5) output_filename = NULL;\r
+        }\r
+        if (!output_filename) { badusage(exename); return 1; }\r
+    }\r
+\r
+    if (decode) return decodeFile(input_filename, output_filename);\r
+\r
+    // compression is default action\r
+    if (legacy_format)\r
+    {\r
+        DISPLAY("! Generating compressed LZ4 using Legacy format (deprecated !) ! \n");\r
+        return legacy_compress_file(input_filename, output_filename, cLevel);   \r
+    }\r
+    else\r
+    {\r
+        return compress_file(input_filename, output_filename, cLevel);   \r
+    }\r
+}\r
diff --git a/silo/third-party/lz4/lz4hc.c b/silo/third-party/lz4/lz4hc.c
new file mode 100644 (file)
index 0000000..729bfd3
--- /dev/null
@@ -0,0 +1,584 @@
+/*\r
+   LZ4 HC - High Compression Mode of LZ4\r
+   Copyright (C) 2011-2013, Yann Collet.\r
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+   Redistribution and use in source and binary forms, with or without\r
+   modification, are permitted provided that the following conditions are\r
+   met:\r
+\r
+       * Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+       * Redistributions in binary form must reproduce the above\r
+   copyright notice, this list of conditions and the following disclaimer\r
+   in the documentation and/or other materials provided with the\r
+   distribution.\r
+\r
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+   You can contact the author at :\r
+   - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+   - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+\r
+/*\r
+Note : this source file requires "lz4hc_encoder.h"\r
+*/\r
+\r
+\r
+//**************************************\r
+// Memory routines\r
+//**************************************\r
+#include <stdlib.h>   // calloc, free\r
+#define ALLOCATOR(s)  calloc(1,s)\r
+#define FREEMEM       free\r
+#include <string.h>   // memset, memcpy\r
+#define MEM_INIT      memset\r
+\r
+\r
+//**************************************\r
+// CPU Feature Detection\r
+//**************************************\r
+// 32 or 64 bits ?\r
+#if (defined(__x86_64__) || defined(_M_X64) || defined(_WIN64) \\r
+  || defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) \\r
+  || defined(__64BIT__) || defined(_LP64) || defined(__LP64__) \\r
+  || defined(__ia64) || defined(__itanium__) || defined(_M_IA64) )   // Detects 64 bits mode\r
+#  define LZ4_ARCH64 1\r
+#else\r
+#  define LZ4_ARCH64 0\r
+#endif\r
+\r
+// Little Endian or Big Endian ?\r
+// Overwrite the #define below if you know your architecture endianess\r
+#if defined (__GLIBC__)\r
+#  include <endian.h>\r
+#  if (__BYTE_ORDER == __BIG_ENDIAN)\r
+#     define LZ4_BIG_ENDIAN 1\r
+#  endif\r
+#elif (defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN))\r
+#  define LZ4_BIG_ENDIAN 1\r
+#elif defined(__sparc) || defined(__sparc__) \\r
+   || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) \\r
+   || defined(__hpux)  || defined(__hppa) \\r
+   || defined(_MIPSEB) || defined(__s390__)\r
+#  define LZ4_BIG_ENDIAN 1\r
+#else\r
+// Little Endian assumed. PDP Endian and other very rare endian format are unsupported.\r
+#endif\r
+\r
+// Unaligned memory access is automatically enabled for "common" CPU, such as x86.\r
+// For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected\r
+// If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance\r
+#if defined(__ARM_FEATURE_UNALIGNED)\r
+#  define LZ4_FORCE_UNALIGNED_ACCESS 1\r
+#endif\r
+\r
+// Define this parameter if your target system or compiler does not support hardware bit count\r
+#if defined(_MSC_VER) && defined(_WIN32_WCE)            // Visual Studio for Windows CE does not support Hardware bit count\r
+#  define LZ4_FORCE_SW_BITCOUNT\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Compiler Options\r
+//**************************************\r
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99\r
+  /* "restrict" is a known keyword */\r
+#else\r
+#  define restrict  // Disable restrict\r
+#endif\r
+\r
+#ifdef _MSC_VER\r
+#  define forceinline __forceinline\r
+#  include <intrin.h>                 // For Visual 2005\r
+#  if LZ4_ARCH64     // 64-bits\r
+#    pragma intrinsic(_BitScanForward64) // For Visual 2005\r
+#    pragma intrinsic(_BitScanReverse64) // For Visual 2005\r
+#  else              // 32-bits\r
+#    pragma intrinsic(_BitScanForward)   // For Visual 2005\r
+#    pragma intrinsic(_BitScanReverse)   // For Visual 2005\r
+#  endif\r
+#  pragma warning(disable : 4127)        // disable: C4127: conditional expression is constant\r
+#  pragma warning(disable : 4701)        // disable: C4701: potentially uninitialized local variable used\r
+#else \r
+#  ifdef __GNUC__\r
+#    define forceinline inline __attribute__((always_inline))\r
+#  else\r
+#    define forceinline inline\r
+#  endif\r
+#endif\r
+\r
+#ifdef _MSC_VER  // Visual Studio\r
+#define lz4_bswap16(x) _byteswap_ushort(x)\r
+#else\r
+#define lz4_bswap16(x)  ((unsigned short int) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)))\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Includes\r
+//**************************************\r
+#include "lz4hc.h"\r
+#include "lz4.h"\r
+\r
+\r
+//**************************************\r
+// Basic Types\r
+//**************************************\r
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99\r
+# include <stdint.h>\r
+  typedef uint8_t  BYTE;\r
+  typedef uint16_t U16;\r
+  typedef uint32_t U32;\r
+  typedef  int32_t S32;\r
+  typedef uint64_t U64;\r
+#else\r
+  typedef unsigned char       BYTE;\r
+  typedef unsigned short      U16;\r
+  typedef unsigned int        U32;\r
+  typedef   signed int        S32;\r
+  typedef unsigned long long  U64;\r
+#endif\r
+\r
+#if defined(__GNUC__)  && !defined(LZ4_FORCE_UNALIGNED_ACCESS)\r
+#  define _PACKED __attribute__ ((packed))\r
+#else\r
+#  define _PACKED\r
+#endif\r
+\r
+#if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__)\r
+#  ifdef __IBMC__\r
+#    pragma pack(1)\r
+#  else\r
+#    pragma pack(push, 1)\r
+#  endif\r
+#endif\r
+\r
+typedef struct _U16_S { U16 v; } _PACKED U16_S;\r
+typedef struct _U32_S { U32 v; } _PACKED U32_S;\r
+typedef struct _U64_S { U64 v; } _PACKED U64_S;\r
+\r
+#if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__)\r
+#  pragma pack(pop)\r
+#endif\r
+\r
+#define A64(x) (((U64_S *)(x))->v)\r
+#define A32(x) (((U32_S *)(x))->v)\r
+#define A16(x) (((U16_S *)(x))->v)\r
+\r
+\r
+//**************************************\r
+// Constants\r
+//**************************************\r
+#define MINMATCH 4\r
+\r
+#define DICTIONARY_LOGSIZE 16\r
+#define MAXD (1<<DICTIONARY_LOGSIZE)\r
+#define MAXD_MASK ((U32)(MAXD - 1))\r
+#define MAX_DISTANCE (MAXD - 1)\r
+\r
+#define HASH_LOG (DICTIONARY_LOGSIZE-1)\r
+#define HASHTABLESIZE (1 << HASH_LOG)\r
+#define HASH_MASK (HASHTABLESIZE - 1)\r
+\r
+#define MAX_NB_ATTEMPTS 256\r
+\r
+#define ML_BITS  4\r
+#define ML_MASK  (size_t)((1U<<ML_BITS)-1)\r
+#define RUN_BITS (8-ML_BITS)\r
+#define RUN_MASK ((1U<<RUN_BITS)-1)\r
+\r
+#define COPYLENGTH 8\r
+#define LASTLITERALS 5\r
+#define MFLIMIT (COPYLENGTH+MINMATCH)\r
+#define MINLENGTH (MFLIMIT+1)\r
+#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH)\r
+\r
+#define KB *(1U<<10)\r
+#define MB *(1U<<20)\r
+#define GB *(1U<<30)\r
+\r
+\r
+//**************************************\r
+// Architecture-specific macros\r
+//**************************************\r
+#if LZ4_ARCH64   // 64-bit\r
+#  define STEPSIZE 8\r
+#  define LZ4_COPYSTEP(s,d)     A64(d) = A64(s); d+=8; s+=8;\r
+#  define LZ4_COPYPACKET(s,d)   LZ4_COPYSTEP(s,d)\r
+#  define UARCH U64\r
+#  define AARCH A64\r
+#  define HTYPE                 U32\r
+#  define INITBASE(b,s)         const BYTE* const b = s\r
+#else   // 32-bit\r
+#  define STEPSIZE 4\r
+#  define LZ4_COPYSTEP(s,d)     A32(d) = A32(s); d+=4; s+=4;\r
+#  define LZ4_COPYPACKET(s,d)   LZ4_COPYSTEP(s,d); LZ4_COPYSTEP(s,d);\r
+#  define UARCH U32\r
+#  define AARCH A32\r
+//#  define HTYPE                 const BYTE*\r
+//#  define INITBASE(b,s)         const int b = 0\r
+#  define HTYPE                 U32\r
+#  define INITBASE(b,s)         const BYTE* const b = s\r
+#endif\r
+\r
+#if defined(LZ4_BIG_ENDIAN)\r
+#  define LZ4_READ_LITTLEENDIAN_16(d,s,p) { U16 v = A16(p); v = lz4_bswap16(v); d = (s) - v; }\r
+#  define LZ4_WRITE_LITTLEENDIAN_16(p,i)  { U16 v = (U16)(i); v = lz4_bswap16(v); A16(p) = v; p+=2; }\r
+#else   // Little Endian\r
+#  define LZ4_READ_LITTLEENDIAN_16(d,s,p) { d = (s) - A16(p); }\r
+#  define LZ4_WRITE_LITTLEENDIAN_16(p,v)  { A16(p) = v; p+=2; }\r
+#endif\r
+\r
+\r
+//************************************************************\r
+// Local Types\r
+//************************************************************\r
+typedef struct \r
+{\r
+    const BYTE* inputBuffer;\r
+    const BYTE* base;\r
+    const BYTE* end;\r
+    HTYPE hashTable[HASHTABLESIZE];\r
+    U16 chainTable[MAXD];\r
+    const BYTE* nextToUpdate;\r
+} LZ4HC_Data_Structure;\r
+\r
+\r
+//**************************************\r
+// Macros\r
+//**************************************\r
+#define LZ4_WILDCOPY(s,d,e)    do { LZ4_COPYPACKET(s,d) } while (d<e);\r
+#define LZ4_BLINDCOPY(s,d,l)   { BYTE* e=d+l; LZ4_WILDCOPY(s,d,e); d=e; }\r
+#define HASH_FUNCTION(i)       (((i) * 2654435761U) >> ((MINMATCH*8)-HASH_LOG))\r
+#define HASH_VALUE(p)          HASH_FUNCTION(A32(p))\r
+#define HASH_POINTER(p)        (HashTable[HASH_VALUE(p)] + base)\r
+#define DELTANEXT(p)           chainTable[(size_t)(p) & MAXD_MASK] \r
+#define GETNEXT(p)             ((p) - (size_t)DELTANEXT(p))\r
+\r
+\r
+//**************************************\r
+// Private functions\r
+//**************************************\r
+#if LZ4_ARCH64\r
+\r
+inline static int LZ4_NbCommonBytes (register U64 val)\r
+{\r
+#if defined(LZ4_BIG_ENDIAN)\r
+#  if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    unsigned long r = 0;\r
+    _BitScanReverse64( &r, val );\r
+    return (int)(r>>3);\r
+#  elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    return (__builtin_clzll(val) >> 3); \r
+#  else\r
+    int r;\r
+    if (!(val>>32)) { r=4; } else { r=0; val>>=32; }\r
+    if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }\r
+    r += (!val);\r
+    return r;\r
+#  endif\r
+#else\r
+#  if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    unsigned long r = 0;\r
+    _BitScanForward64( &r, val );\r
+    return (int)(r>>3);\r
+#  elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    return (__builtin_ctzll(val) >> 3); \r
+#  else\r
+    static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };\r
+    return DeBruijnBytePos[((U64)((val & -val) * 0x0218A392CDABBD3F)) >> 58];\r
+#  endif\r
+#endif\r
+}\r
+\r
+#else\r
+\r
+inline static int LZ4_NbCommonBytes (register U32 val)\r
+{\r
+#if defined(LZ4_BIG_ENDIAN)\r
+#  if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    unsigned long r;\r
+    _BitScanReverse( &r, val );\r
+    return (int)(r>>3);\r
+#  elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    return (__builtin_clz(val) >> 3); \r
+#  else\r
+    int r;\r
+    if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }\r
+    r += (!val);\r
+    return r;\r
+#  endif\r
+#else\r
+#  if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    unsigned long r;\r
+    _BitScanForward( &r, val );\r
+    return (int)(r>>3);\r
+#  elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)\r
+    return (__builtin_ctz(val) >> 3); \r
+#  else\r
+    static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };\r
+    return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];\r
+#  endif\r
+#endif\r
+}\r
+\r
+#endif\r
+\r
+\r
+static inline int LZ4_InitHC (LZ4HC_Data_Structure* hc4, const BYTE* base)\r
+{\r
+    MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable));\r
+    MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable));\r
+    hc4->nextToUpdate = base + 1;\r
+    hc4->base = base;\r
+    hc4->inputBuffer = base;\r
+    hc4->end = base;\r
+    return 1;\r
+}\r
+\r
+\r
+void* LZ4_createHC (const char* slidingInputBuffer)\r
+{\r
+    void* hc4 = ALLOCATOR(sizeof(LZ4HC_Data_Structure));\r
+    LZ4_InitHC ((LZ4HC_Data_Structure*)hc4, (const BYTE*)slidingInputBuffer);\r
+    return hc4;\r
+}\r
+\r
+\r
+int LZ4_freeHC (void* LZ4HC_Data)\r
+{\r
+    FREEMEM(LZ4HC_Data);\r
+    return (0);\r
+}\r
+\r
+\r
+// Update chains up to ip (excluded)\r
+static forceinline void LZ4HC_Insert (LZ4HC_Data_Structure* hc4, const BYTE* ip)\r
+{\r
+    U16*   chainTable = hc4->chainTable;\r
+    HTYPE* HashTable  = hc4->hashTable;\r
+    INITBASE(base,hc4->base);\r
+\r
+    while(hc4->nextToUpdate < ip)\r
+    {\r
+        const BYTE* const p = hc4->nextToUpdate;\r
+        size_t delta = (p) - HASH_POINTER(p); \r
+        if (delta>MAX_DISTANCE) delta = MAX_DISTANCE; \r
+        DELTANEXT(p) = (U16)delta; \r
+        HashTable[HASH_VALUE(p)] = (HTYPE)((p) - base);\r
+        hc4->nextToUpdate++;\r
+    }\r
+}\r
+\r
+\r
+char* LZ4_slideInputBufferHC(void* LZ4HC_Data)\r
+{\r
+    LZ4HC_Data_Structure* hc4 = (LZ4HC_Data_Structure*)LZ4HC_Data;\r
+    U32 distance = (U32)(hc4->end - hc4->inputBuffer) - 64 KB;\r
+    distance = (distance >> 16) << 16;   // Must be a multiple of 64 KB\r
+    LZ4HC_Insert(hc4, hc4->end - MINMATCH);\r
+    memcpy((void*)(hc4->end - 64 KB - distance), (const void*)(hc4->end - 64 KB), 64 KB);\r
+    hc4->nextToUpdate -= distance;\r
+    hc4->base -= distance;\r
+    if ((U32)(hc4->inputBuffer - hc4->base) > 1 GB + 64 KB)   // Avoid overflow\r
+    {\r
+        int i;\r
+        hc4->base += 1 GB;\r
+        for (i=0; i<HASHTABLESIZE; i++) hc4->hashTable[i] -= 1 GB;\r
+    }\r
+    hc4->end -= distance;\r
+    return (char*)(hc4->end);\r
+}\r
+\r
+\r
+static forceinline size_t LZ4HC_CommonLength (const BYTE* p1, const BYTE* p2, const BYTE* const matchlimit)\r
+{\r
+    const BYTE* p1t = p1;\r
+\r
+    while (p1t<matchlimit-(STEPSIZE-1))\r
+    {\r
+        UARCH diff = AARCH(p2) ^ AARCH(p1t);\r
+        if (!diff) { p1t+=STEPSIZE; p2+=STEPSIZE; continue; }\r
+        p1t += LZ4_NbCommonBytes(diff);\r
+        return (p1t - p1);\r
+    }\r
+    if (LZ4_ARCH64) if ((p1t<(matchlimit-3)) && (A32(p2) == A32(p1t))) { p1t+=4; p2+=4; }\r
+    if ((p1t<(matchlimit-1)) && (A16(p2) == A16(p1t))) { p1t+=2; p2+=2; }\r
+    if ((p1t<matchlimit) && (*p2 == *p1t)) p1t++;\r
+    return (p1t - p1);\r
+}\r
+\r
+\r
+static forceinline int LZ4HC_InsertAndFindBestMatch (LZ4HC_Data_Structure* hc4, const BYTE* ip, const BYTE* const matchlimit, const BYTE** matchpos)\r
+{\r
+    U16* const chainTable = hc4->chainTable;\r
+    HTYPE* const HashTable = hc4->hashTable;\r
+    const BYTE* ref;\r
+    INITBASE(base,hc4->base);\r
+    int nbAttempts=MAX_NB_ATTEMPTS;\r
+    size_t repl=0, ml=0;\r
+    U16 delta=0;  // useless assignment, to remove an uninitialization warning\r
+\r
+    // HC4 match finder\r
+    LZ4HC_Insert(hc4, ip);\r
+    ref = HASH_POINTER(ip);\r
+\r
+#define REPEAT_OPTIMIZATION\r
+#ifdef REPEAT_OPTIMIZATION\r
+    // Detect repetitive sequences of length <= 4\r
+    if ((U32)(ip-ref) <= 4)        // potential repetition\r
+    {\r
+        if (A32(ref) == A32(ip))   // confirmed\r
+        {\r
+            delta = (U16)(ip-ref);\r
+            repl = ml  = LZ4HC_CommonLength(ip+MINMATCH, ref+MINMATCH, matchlimit) + MINMATCH;\r
+            *matchpos = ref;\r
+        }\r
+        ref = GETNEXT(ref);\r
+    }\r
+#endif\r
+\r
+    while (((U32)(ip-ref) <= MAX_DISTANCE) && (nbAttempts))\r
+    {\r
+        nbAttempts--;\r
+        if (*(ref+ml) == *(ip+ml))\r
+        if (A32(ref) == A32(ip))\r
+        {\r
+            size_t mlt = LZ4HC_CommonLength(ip+MINMATCH, ref+MINMATCH, matchlimit) + MINMATCH;\r
+            if (mlt > ml) { ml = mlt; *matchpos = ref; }\r
+        }\r
+        ref = GETNEXT(ref);\r
+    }\r
+\r
+#ifdef REPEAT_OPTIMIZATION\r
+    // Complete table\r
+    if (repl)\r
+    {\r
+        const BYTE* ptr = ip;\r
+        const BYTE* end;\r
+\r
+        end = ip + repl - (MINMATCH-1);\r
+        while(ptr < end-delta)\r
+        {\r
+            DELTANEXT(ptr) = delta;    // Pre-Load\r
+            ptr++;\r
+        }\r
+        do\r
+        {\r
+            DELTANEXT(ptr) = delta;    \r
+            HashTable[HASH_VALUE(ptr)] = (HTYPE)((ptr) - base);     // Head of chain\r
+            ptr++;\r
+        } while(ptr < end);\r
+        hc4->nextToUpdate = end;\r
+    }\r
+#endif \r
+\r
+    return (int)ml;\r
+}\r
+\r
+\r
+static forceinline int LZ4HC_InsertAndGetWiderMatch (LZ4HC_Data_Structure* hc4, const BYTE* ip, const BYTE* startLimit, const BYTE* matchlimit, int longest, const BYTE** matchpos, const BYTE** startpos)\r
+{\r
+    U16* const  chainTable = hc4->chainTable;\r
+    HTYPE* const HashTable = hc4->hashTable;\r
+    INITBASE(base,hc4->base);\r
+    const BYTE*  ref;\r
+    int nbAttempts = MAX_NB_ATTEMPTS;\r
+    int delta = (int)(ip-startLimit);\r
+\r
+    // First Match\r
+    LZ4HC_Insert(hc4, ip);\r
+    ref = HASH_POINTER(ip);\r
+\r
+    while (((U32)(ip-ref) <= MAX_DISTANCE) && (nbAttempts))\r
+    {\r
+        nbAttempts--;\r
+        if (*(startLimit + longest) == *(ref - delta + longest))\r
+        if (A32(ref) == A32(ip))\r
+        {\r
+#if 1\r
+            const BYTE* reft = ref+MINMATCH;\r
+            const BYTE* ipt = ip+MINMATCH;\r
+            const BYTE* startt = ip;\r
+\r
+            while (ipt<matchlimit-(STEPSIZE-1))\r
+            {\r
+                UARCH diff = AARCH(reft) ^ AARCH(ipt);\r
+                if (!diff) { ipt+=STEPSIZE; reft+=STEPSIZE; continue; }\r
+                ipt += LZ4_NbCommonBytes(diff);\r
+                goto _endCount;\r
+            }\r
+            if (LZ4_ARCH64) if ((ipt<(matchlimit-3)) && (A32(reft) == A32(ipt))) { ipt+=4; reft+=4; }\r
+            if ((ipt<(matchlimit-1)) && (A16(reft) == A16(ipt))) { ipt+=2; reft+=2; }\r
+            if ((ipt<matchlimit) && (*reft == *ipt)) ipt++;\r
+_endCount:\r
+            reft = ref;\r
+#else\r
+            // Easier for code maintenance, but unfortunately slower too\r
+            const BYTE* startt = ip;\r
+            const BYTE* reft = ref;\r
+            const BYTE* ipt = ip + MINMATCH + LZ4HC_CommonLength(ip+MINMATCH, ref+MINMATCH, matchlimit);\r
+#endif\r
+\r
+            while ((startt>startLimit) && (reft > hc4->inputBuffer) && (startt[-1] == reft[-1])) {startt--; reft--;}\r
+\r
+            if ((ipt-startt) > longest)\r
+            {\r
+                longest = (int)(ipt-startt);\r
+                *matchpos = reft;\r
+                *startpos = startt;\r
+            }\r
+        }\r
+        ref = GETNEXT(ref);\r
+    }\r
+\r
+    return longest;\r
+}\r
+\r
+\r
+\r
+//**************************************\r
+// Compression functions\r
+//**************************************\r
+\r
+/*\r
+int LZ4_compressHC(\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest'.\r
+Destination buffer must be already allocated, and sized at a minimum of LZ4_compressBound(inputSize).\r
+return : the number of bytes written in buffer 'dest'\r
+*/\r
+#define FUNCTION_NAME LZ4_compressHC\r
+#include "lz4hc_encoder.h"\r
+\r
+\r
+/*\r
+int LZ4_compressHC_limitedOutput(\r
+                 const char* source,\r
+                 char* dest,\r
+                 int inputSize,\r
+                 int maxOutputSize)\r
+\r
+Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.\r
+If it cannot achieve it, compression will stop, and result of the function will be zero.\r
+return : the number of bytes written in buffer 'dest', or 0 if the compression fails\r
+*/\r
+#define FUNCTION_NAME LZ4_compressHC_limitedOutput\r
+#define LIMITED_OUTPUT\r
+#include "lz4hc_encoder.h"\r
+\r
diff --git a/silo/third-party/lz4/lz4hc.h b/silo/third-party/lz4/lz4hc.h
new file mode 100644 (file)
index 0000000..7db2160
--- /dev/null
@@ -0,0 +1,111 @@
+/*\r
+   LZ4 HC - High Compression Mode of LZ4\r
+   Header File\r
+   Copyright (C) 2011-2013, Yann Collet.\r
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+   Redistribution and use in source and binary forms, with or without\r
+   modification, are permitted provided that the following conditions are\r
+   met:\r
+\r
+       * Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+       * Redistributions in binary form must reproduce the above\r
+   copyright notice, this list of conditions and the following disclaimer\r
+   in the documentation and/or other materials provided with the\r
+   distribution.\r
+\r
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+   You can contact the author at :\r
+   - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+   - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+#pragma once\r
+\r
+\r
+#if defined (__cplusplus)\r
+extern "C" {\r
+#endif\r
+\r
+\r
+int LZ4_compressHC (const char* source, char* dest, int inputSize);\r
+/*\r
+LZ4_compressHC :\r
+    return : the number of bytes in compressed buffer dest\r
+             or 0 if compression fails.\r
+    note : destination buffer must be already allocated. \r
+        To avoid any problem, size it to handle worst cases situations (input data not compressible)\r
+        Worst case size evaluation is provided by function LZ4_compressBound() (see "lz4.h")\r
+*/\r
+\r
+int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize);\r
+/*\r
+LZ4_compress_limitedOutput() :\r
+    Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.\r
+    If it cannot achieve it, compression will stop, and result of the function will be zero.\r
+    This function never writes outside of provided output buffer.\r
+\r
+    inputSize  : Max supported value is 1 GB\r
+    maxOutputSize : is maximum allowed size into the destination buffer (which must be already allocated)\r
+    return : the number of output bytes written in buffer 'dest'\r
+             or 0 if compression fails.\r
+*/\r
+\r
+\r
+/* Note :\r
+Decompression functions are provided within LZ4 source code (see "lz4.h") (BSD license)\r
+*/\r
+\r
+\r
+/* Advanced Functions */\r
+\r
+void* LZ4_createHC (const char* slidingInputBuffer);\r
+int   LZ4_compressHC_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize);\r
+int   LZ4_compressHC_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize);\r
+char* LZ4_slideInputBufferHC (void* LZ4HC_Data);\r
+int   LZ4_freeHC (void* LZ4HC_Data);\r
+\r
+/* \r
+These functions allow the compression of dependent blocks, where each block benefits from prior 64 KB within preceding blocks.\r
+In order to achieve this, it is necessary to start creating the LZ4HC Data Structure, thanks to the function :\r
+\r
+void* LZ4_createHC (const char* slidingInputBuffer);\r
+The result of the function is the (void*) pointer on the LZ4HC Data Structure.\r
+This pointer will be needed in all other functions.\r
+If the pointer returned is NULL, then the allocation has failed, and compression must be aborted.\r
+The only parameter 'const char* slidingInputBuffer' must, obviously, point at the beginning of input buffer.\r
+The input buffer must be already allocated, and size at least 192KB.\r
+'slidingInputBuffer' will also be the 'const char* source' of the first block.\r
+\r
+All blocks are expected to lay next to each other within the input buffer, starting from 'slidingInputBuffer'.\r
+To compress each block, use either LZ4_compressHC_continue() or LZ4_compressHC_limitedOutput_continue().\r
+Their behavior are identical to LZ4_compressHC() or LZ4_compressHC_limitedOutput(), \r
+but require the LZ4HC Data Structure as their first argument, and check that each block starts right after the previous one.\r
+If next block does not begin immediately after the previous one, the compression will fail (return 0).\r
+\r
+When it's no longer possible to lay the next block after the previous one (not enough space left into input buffer), a call to : \r
+char* LZ4_slideInputBufferHC(void* LZ4HC_Data);\r
+must be performed. It will typically copy the latest 64KB of input at the beginning of input buffer.\r
+Note that, for this function to work properly, minimum size of an input buffer must be 192KB.\r
+==> The memory position where the next input data block must start is provided as the result of the function.\r
+\r
+Compression can then resume, using LZ4_compressHC_continue() or LZ4_compressHC_limitedOutput_continue(), as usual.\r
+\r
+When compression is completed, a call to LZ4_freeHC() will release the memory used by the LZ4HC Data Structure.\r
+*/\r
+\r
+\r
+#if defined (__cplusplus)\r
+}\r
+#endif\r
diff --git a/silo/third-party/lz4/lz4hc_encoder.h b/silo/third-party/lz4/lz4hc_encoder.h
new file mode 100644 (file)
index 0000000..b59bef3
--- /dev/null
@@ -0,0 +1,349 @@
+/*\r
+   LZ4 HC Encoder - Part of LZ4 HC algorithm\r
+   Copyright (C) 2011-2013, Yann Collet.\r
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+   Redistribution and use in source and binary forms, with or without\r
+   modification, are permitted provided that the following conditions are\r
+   met:\r
+\r
+       * Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+       * Redistributions in binary form must reproduce the above\r
+   copyright notice, this list of conditions and the following disclaimer\r
+   in the documentation and/or other materials provided with the\r
+   distribution.\r
+\r
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+   You can contact the author at :\r
+   - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html\r
+   - LZ4 source repository : http://code.google.com/p/lz4/\r
+*/\r
+\r
+/* lz4hc_encoder.h must be included into lz4hc.c\r
+   The objective of this file is to create a single LZ4 compression function source\r
+   which will be instanciated multiple times with minor variations\r
+   depending on a set of #define.\r
+*/\r
+\r
+//****************************\r
+// Check required defines\r
+//****************************\r
+\r
+#ifndef FUNCTION_NAME\r
+#  error "FUNTION_NAME is not defined"\r
+#endif\r
+\r
+\r
+//****************************\r
+// Local definitions\r
+//****************************\r
+#define COMBINED_NAME_RAW(n1,n2) n1 ## n2\r
+#define COMBINED_NAME(n1,n2) COMBINED_NAME_RAW(n1,n2)\r
+#define ENCODE_SEQUENCE_NAME COMBINED_NAME(FUNCTION_NAME,_encodeSequence)\r
+#ifdef LIMITED_OUTPUT\r
+#  define ENCODE_SEQUENCE(i,o,a,m,r,d) if (ENCODE_SEQUENCE_NAME(i,o,a,m,r,d)) return 0;\r
+#else\r
+#  define ENCODE_SEQUENCE(i,o,a,m,r,d) ENCODE_SEQUENCE_NAME(i,o,a,m,r)\r
+#endif\r
+\r
+//****************************\r
+// Function code\r
+//****************************\r
+\r
+forceinline static int ENCODE_SEQUENCE_NAME (\r
+                       const BYTE** ip, \r
+                       BYTE** op, \r
+                       const BYTE** anchor, \r
+                       int matchLength, \r
+                       const BYTE* ref\r
+#ifdef LIMITED_OUTPUT\r
+                      ,BYTE* oend\r
+#endif\r
+                       )\r
+{\r
+    int length, len; \r
+    BYTE* token;\r
+\r
+    // Encode Literal length\r
+    length = (int)(*ip - *anchor);\r
+    token = (*op)++;\r
+#ifdef LIMITED_OUTPUT\r
+    if ((*op + length + (2 + 1 + LASTLITERALS) + (length>>8)) > oend) return 1;                // Check output limit\r
+#endif\r
+    if (length>=(int)RUN_MASK) { *token=(RUN_MASK<<ML_BITS); len = length-RUN_MASK; for(; len > 254 ; len-=255) *(*op)++ = 255;  *(*op)++ = (BYTE)len; } \r
+    else *token = (BYTE)(length<<ML_BITS);\r
+\r
+    // Copy Literals\r
+    LZ4_BLINDCOPY(*anchor, *op, length);\r
+\r
+    // Encode Offset\r
+    LZ4_WRITE_LITTLEENDIAN_16(*op,(U16)(*ip-ref));\r
+\r
+    // Encode MatchLength\r
+    length = (int)(matchLength-MINMATCH);\r
+#ifdef LIMITED_OUTPUT\r
+    if (*op + (1 + LASTLITERALS) + (length>>8) > oend) return 1;               // Check output limit\r
+#endif\r
+    if (length>=(int)ML_MASK) { *token+=ML_MASK; length-=ML_MASK; for(; length > 509 ; length-=510) { *(*op)++ = 255; *(*op)++ = 255; } if (length > 254) { length-=255; *(*op)++ = 255; } *(*op)++ = (BYTE)length; } \r
+    else *token += (BYTE)(length);     \r
+\r
+    // Prepare next loop\r
+    *ip += matchLength;\r
+    *anchor = *ip; \r
+\r
+    return 0;\r
+}\r
+\r
+\r
+int COMBINED_NAME(FUNCTION_NAME,_continue) (\r
+                 void* ctxvoid,\r
+                 const char* source, \r
+                 char* dest,\r
+                 int inputSize\r
+#ifdef LIMITED_OUTPUT\r
+                ,int maxOutputSize\r
+#endif\r
+                )\r
+{\r
+    LZ4HC_Data_Structure* ctx = (LZ4HC_Data_Structure*) ctxvoid;\r
+    const BYTE* ip = (const BYTE*) source;\r
+    const BYTE* anchor = ip;\r
+    const BYTE* const iend = ip + inputSize;\r
+    const BYTE* const mflimit = iend - MFLIMIT;\r
+    const BYTE* const matchlimit = (iend - LASTLITERALS);\r
+\r
+    BYTE* op = (BYTE*) dest;\r
+#ifdef LIMITED_OUTPUT\r
+    BYTE* const oend = op + maxOutputSize;\r
+#endif\r
+\r
+    int        ml, ml2, ml3, ml0;\r
+    const BYTE* ref=NULL;\r
+    const BYTE* start2=NULL;\r
+    const BYTE* ref2=NULL;\r
+    const BYTE* start3=NULL;\r
+    const BYTE* ref3=NULL;\r
+    const BYTE* start0;\r
+    const BYTE* ref0;\r
+\r
+    // Ensure blocks follow each other\r
+    if (ip != ctx->end) return 0;\r
+    ctx->end += inputSize;\r
+\r
+    ip++;\r
+\r
+    // Main Loop\r
+    while (ip < mflimit)\r
+    {\r
+        ml = LZ4HC_InsertAndFindBestMatch (ctx, ip, matchlimit, (&ref));\r
+        if (!ml) { ip++; continue; }\r
+\r
+        // saved, in case we would skip too much\r
+        start0 = ip;\r
+        ref0 = ref;\r
+        ml0 = ml;\r
+\r
+_Search2:\r
+        if (ip+ml < mflimit)\r
+            ml2 = LZ4HC_InsertAndGetWiderMatch(ctx, ip + ml - 2, ip + 1, matchlimit, ml, &ref2, &start2);\r
+        else ml2 = ml;\r
+\r
+        if (ml2 == ml)  // No better match\r
+        {\r
+            ENCODE_SEQUENCE(&ip, &op, &anchor, ml, ref, oend);\r
+            continue;\r
+        }\r
+\r
+        if (start0 < ip)\r
+        {\r
+            if (start2 < ip + ml0)   // empirical\r
+            {\r
+                ip = start0;\r
+                ref = ref0;\r
+                ml = ml0;\r
+            }\r
+        }\r
+\r
+        // Here, start0==ip\r
+        if ((start2 - ip) < 3)   // First Match too small : removed\r
+        {\r
+            ml = ml2;\r
+            ip = start2;\r
+            ref =ref2;\r
+            goto _Search2;\r
+        }\r
+\r
+_Search3:\r
+        // Currently we have :\r
+        // ml2 > ml1, and\r
+        // ip1+3 <= ip2 (usually < ip1+ml1)\r
+        if ((start2 - ip) < OPTIMAL_ML)\r
+        {\r
+            int correction;\r
+            int new_ml = ml;\r
+            if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML;\r
+            if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH;\r
+            correction = new_ml - (int)(start2 - ip);\r
+            if (correction > 0)\r
+            {\r
+                start2 += correction;\r
+                ref2 += correction;\r
+                ml2 -= correction;\r
+            }\r
+        }\r
+        // Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18)\r
+\r
+        if (start2 + ml2 < mflimit)\r
+            ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3);\r
+        else ml3 = ml2;\r
+\r
+        if (ml3 == ml2) // No better match : 2 sequences to encode\r
+        {\r
+            // ip & ref are known; Now for ml\r
+            if (start2 < ip+ml)  ml = (int)(start2 - ip);\r
+            // Now, encode 2 sequences\r
+            ENCODE_SEQUENCE(&ip, &op, &anchor, ml, ref, oend);\r
+            ip = start2;\r
+            ENCODE_SEQUENCE(&ip, &op, &anchor, ml2, ref2, oend);\r
+            continue;\r
+        }\r
+\r
+        if (start3 < ip+ml+3) // Not enough space for match 2 : remove it\r
+        {\r
+            if (start3 >= (ip+ml)) // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1\r
+            {\r
+                if (start2 < ip+ml)\r
+                {\r
+                    int correction = (int)(ip+ml - start2);\r
+                    start2 += correction;\r
+                    ref2 += correction;\r
+                    ml2 -= correction;\r
+                    if (ml2 < MINMATCH)\r
+                    {\r
+                        start2 = start3;\r
+                        ref2 = ref3;\r
+                        ml2 = ml3;\r
+                    }\r
+                }\r
+\r
+                ENCODE_SEQUENCE(&ip, &op, &anchor, ml, ref, oend);\r
+                ip  = start3;\r
+                ref = ref3;\r
+                ml  = ml3;\r
+\r
+                start0 = start2;\r
+                ref0 = ref2;\r
+                ml0 = ml2;\r
+                goto _Search2;\r
+            }\r
+\r
+            start2 = start3;\r
+            ref2 = ref3;\r
+            ml2 = ml3;\r
+            goto _Search3;\r
+        }\r
+\r
+        // OK, now we have 3 ascending matches; let's write at least the first one\r
+        // ip & ref are known; Now for ml\r
+        if (start2 < ip+ml)\r
+        {\r
+            if ((start2 - ip) < (int)ML_MASK)\r
+            {\r
+                int correction;\r
+                if (ml > OPTIMAL_ML) ml = OPTIMAL_ML;\r
+                if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH;\r
+                correction = ml - (int)(start2 - ip);\r
+                if (correction > 0)\r
+                {\r
+                    start2 += correction;\r
+                    ref2 += correction;\r
+                    ml2 -= correction;\r
+                }\r
+            }\r
+            else\r
+            {\r
+                ml = (int)(start2 - ip);\r
+            }\r
+        }\r
+        ENCODE_SEQUENCE(&ip, &op, &anchor, ml, ref, oend);\r
+\r
+        ip = start2;\r
+        ref = ref2;\r
+        ml = ml2;\r
+\r
+        start2 = start3;\r
+        ref2 = ref3;\r
+        ml2 = ml3;\r
+\r
+        goto _Search3;\r
+\r
+    }\r
+\r
+    // Encode Last Literals\r
+    {\r
+        int lastRun = (int)(iend - anchor);\r
+#ifdef LIMITED_OUTPUT\r
+        if (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) return 0;  // Check output limit\r
+#endif\r
+        if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<<ML_BITS); lastRun-=RUN_MASK; for(; lastRun > 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; } \r
+        else *op++ = (BYTE)(lastRun<<ML_BITS);\r
+        memcpy(op, anchor, iend - anchor);\r
+        op += iend-anchor;\r
+    } \r
+\r
+    // End\r
+    return (int) (((char*)op)-dest);\r
+}\r
+\r
+\r
+int FUNCTION_NAME (const char* source, \r
+                 char* dest,\r
+                 int inputSize\r
+#ifdef LIMITED_OUTPUT\r
+                ,int maxOutputSize\r
+#endif\r
+                )\r
+{\r
+    void* ctx = LZ4_createHC(source);\r
+    int result;\r
+    if (ctx==NULL) return 0;\r
+#ifdef LIMITED_OUTPUT\r
+    result = COMBINED_NAME(FUNCTION_NAME,_continue) (ctx, source, dest, inputSize, maxOutputSize);\r
+#else\r
+    result = COMBINED_NAME(FUNCTION_NAME,_continue) (ctx, source, dest, inputSize);\r
+#endif\r
+    LZ4_freeHC(ctx);\r
+\r
+    return result;\r
+}\r
+\r
+\r
+//****************************\r
+// Clean defines\r
+//****************************\r
+\r
+// Required defines\r
+#undef FUNCTION_NAME\r
+\r
+// Locally Generated\r
+#undef ENCODE_SEQUENCE\r
+#undef ENCODE_SEQUENCE_NAME\r
+\r
+// Optional defines\r
+#ifdef LIMITED_OUTPUT\r
+#undef LIMITED_OUTPUT\r
+#endif\r
+\r
+\r
diff --git a/silo/third-party/lz4/xxhash.c b/silo/third-party/lz4/xxhash.c
new file mode 100644 (file)
index 0000000..6dacdcb
--- /dev/null
@@ -0,0 +1,477 @@
+/*\r
+xxHash - Fast Hash algorithm\r
+Copyright (C) 2012-2013, Yann Collet.\r
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+Redistribution and use in source and binary forms, with or without\r
+modification, are permitted provided that the following conditions are\r
+met:\r
+\r
+* Redistributions of source code must retain the above copyright\r
+notice, this list of conditions and the following disclaimer.\r
+* Redistributions in binary form must reproduce the above\r
+copyright notice, this list of conditions and the following disclaimer\r
+in the documentation and/or other materials provided with the\r
+distribution.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+You can contact the author at :\r
+- xxHash source repository : http://code.google.com/p/xxhash/\r
+*/\r
+\r
+\r
+\r
+//**************************************\r
+// Tuning parameters\r
+//**************************************\r
+// Unaligned memory access is automatically enabled for "common" CPU, such as x86.\r
+// For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected.\r
+// If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance.\r
+// You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32).\r
+#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)\r
+#  define XXH_USE_UNALIGNED_ACCESS 1\r
+#endif\r
+\r
+// XXH_ACCEPT_NULL_INPUT_POINTER :\r
+// If the input pointer is a null pointer, xxHash default behavior is to crash, since it is a bad input.\r
+// If this option is enabled, xxHash output for null input pointers will be the same as a null-length input.\r
+// This option has a very small performance cost (only measurable on small inputs).\r
+// By default, this option is disabled. To enable it, uncomment below define :\r
+//#define XXH_ACCEPT_NULL_INPUT_POINTER 1\r
+\r
+// XXH_FORCE_NATIVE_FORMAT :\r
+// By default, xxHash library provides endian-independant Hash values, based on little-endian convention.\r
+// Results are therefore identical for little-endian and big-endian CPU.\r
+// This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.\r
+// Should endian-independance be of no importance for your application, you may uncomment the #define below.\r
+// It will improve speed for Big-endian CPU.\r
+// This option has no impact on Little_Endian CPU.\r
+//#define XXH_FORCE_NATIVE_FORMAT 1\r
+\r
+\r
+//**************************************\r
+// Compiler Options\r
+//**************************************\r
+#if defined(_MSC_VER) && !defined(__cplusplus)   // Visual Studio\r
+#  define inline __inline           // Visual C is not C99, but supports some kind of inline\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Includes & Memory related functions\r
+//**************************************\r
+#include "xxhash.h"\r
+// Modify the local functions below should you wish to use some other memory related routines\r
+// for malloc(), free()\r
+#include <stdlib.h>\r
+static inline void* XXH_malloc(size_t s) { return malloc(s); }\r
+static inline void  XXH_free  (void* p)  { free(p); }\r
+// for memcpy()\r
+#include <string.h>\r
+static inline void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }\r
+\r
+\r
+//**************************************\r
+// CPU Feature Detection\r
+//**************************************\r
+// Little Endian or Big Endian ?\r
+// You can overwrite the #define below if you know your architecture endianess\r
+#if defined(XXH_FORCE_NATIVE_FORMAT) && (XXH_FORCE_NATIVE_FORMAT==1)\r
+// Force native format. The result will be endian dependant.\r
+#  define XXH_BIG_ENDIAN 0\r
+#elif defined (__GLIBC__)\r
+#  include <endian.h>\r
+#  if (__BYTE_ORDER == __BIG_ENDIAN)\r
+#     define XXH_BIG_ENDIAN 1\r
+#  endif\r
+#elif (defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN))\r
+#  define XXH_BIG_ENDIAN 1\r
+#elif defined(__sparc)  || defined(__sparc__) \\r
+    || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) \\r
+    || defined(__hpux)  || defined(__hppa) \\r
+    || defined(_MIPSEB) || defined(__s390__)\r
+#  define XXH_BIG_ENDIAN 1\r
+#endif\r
+\r
+#if !defined(XXH_BIG_ENDIAN)\r
+// Little Endian assumed. PDP Endian and other very rare endian format are unsupported.\r
+#  define XXH_BIG_ENDIAN 0\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Basic Types\r
+//**************************************\r
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   // C99\r
+# include <stdint.h>\r
+  typedef uint8_t  BYTE;\r
+  typedef uint16_t U16;\r
+  typedef uint32_t U32;\r
+  typedef  int32_t S32;\r
+  typedef uint64_t U64;\r
+#else\r
+  typedef unsigned char      BYTE;\r
+  typedef unsigned short     U16;\r
+  typedef unsigned int       U32;\r
+  typedef   signed int       S32;\r
+  typedef unsigned long long U64;\r
+#endif\r
+\r
+#if defined(__GNUC__)  && !defined(XXH_USE_UNALIGNED_ACCESS)\r
+#  define _PACKED __attribute__ ((packed))\r
+#else\r
+#  define _PACKED\r
+#endif\r
+\r
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)\r
+#  pragma pack(push, 1)\r
+#endif\r
+\r
+typedef struct _U32_S { U32 v; } _PACKED U32_S;\r
+\r
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)\r
+#  pragma pack(pop)\r
+#endif\r
+\r
+#define A32(x) (((U32_S *)(x))->v)\r
+\r
+\r
+//***************************************\r
+// Compiler-specific Functions and Macros\r
+//***************************************\r
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)\r
+\r
+// Note : although _rotl exists for minGW (GCC under windows), performance seems poor\r
+#if defined(_MSC_VER)\r
+#  define XXH_rotl32(x,r) _rotl(x,r)\r
+#else\r
+#  define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))\r
+#endif\r
+\r
+#if defined(_MSC_VER)     // Visual Studio\r
+#  define XXH_swap32 _byteswap_ulong\r
+#elif GCC_VERSION >= 403\r
+#  define XXH_swap32 __builtin_bswap32\r
+#else\r
+static inline U32 XXH_swap32 (U32 x) {\r
+    return  ((x << 24) & 0xff000000 ) |\r
+        ((x <<  8) & 0x00ff0000 ) |\r
+        ((x >>  8) & 0x0000ff00 ) |\r
+        ((x >> 24) & 0x000000ff );}\r
+#endif\r
+\r
+\r
+//**************************************\r
+// Constants\r
+//**************************************\r
+#define PRIME32_1   2654435761U\r
+#define PRIME32_2   2246822519U\r
+#define PRIME32_3   3266489917U\r
+#define PRIME32_4    668265263U\r
+#define PRIME32_5    374761393U\r
+\r
+\r
+//**************************************\r
+// Macros\r
+//**************************************\r
+#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(!!(c)) }; }    // use only *after* variable declarations\r
+#define XXH_LE32(p)          (XXH_BIG_ENDIAN ? XXH_swap32(A32(p))     : A32(p))\r
+#define XXH_alignedLE32(p)   (XXH_BIG_ENDIAN ? XXH_swap32(*(U32*)(p)) : *(U32*)(p))\r
+\r
+\r
+\r
+//****************************\r
+// Simple Hash Functions\r
+//****************************\r
+\r
+#if !defined(XXH_USE_UNALIGNED_ACCESS)\r
+// Specific version, for aligned 32-bits input. Useless for CPU supporting unaligned access.\r
+static U32 XXH32_alignedInput(const void* input, int len, U32 seed)\r
+{\r
+    const BYTE* p = (const BYTE*)input;\r
+    const BYTE* const bEnd = p + len;\r
+    U32 h32;\r
+\r
+    if (len>=16)\r
+    {\r
+        const BYTE* const limit = bEnd - 16;\r
+        U32 v1 = seed + PRIME32_1 + PRIME32_2;\r
+        U32 v2 = seed + PRIME32_2;\r
+        U32 v3 = seed + 0;\r
+        U32 v4 = seed - PRIME32_1;\r
+        do\r
+        {\r
+            v1 += XXH_alignedLE32(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;\r
+            v2 += XXH_alignedLE32(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;\r
+            v3 += XXH_alignedLE32(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;\r
+            v4 += XXH_alignedLE32(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;\r
+        } while (p<=limit);\r
+        h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);\r
+    }\r
+    else { h32  = seed + PRIME32_5; }\r
+    h32 += (U32) len;\r
+    while (p<=bEnd-4)\r
+    {\r
+        h32 += XXH_alignedLE32(p) * PRIME32_3;\r
+        h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;\r
+        p+=4;\r
+    }\r
+    while (p<bEnd)\r
+    {\r
+        h32 += (*p) * PRIME32_5;\r
+        h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;\r
+        p++;\r
+    }\r
+    h32 ^= h32 >> 15;\r
+    h32 *= PRIME32_2;\r
+    h32 ^= h32 >> 13;\r
+    h32 *= PRIME32_3;\r
+    h32 ^= h32 >> 16;\r
+    return h32;\r
+}\r
+#endif\r
+\r
+U32 XXH32(const void* input, int len, U32 seed)\r
+{\r
+#if 0\r
+    // Simple version, good for code maintenance, but unfortunately slow for small inputs\r
+    void* state = XXH32_init(seed);\r
+    XXH32_update(state, input, len);\r
+    return XXH32_digest(state);\r
+#else\r
+\r
+    const BYTE* p = (const BYTE*)input;\r
+    const BYTE* const bEnd = p + len;\r
+    U32 h32;\r
+\r
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER\r
+    if (p==NULL) { len=0; p=(const BYTE*)16; }\r
+#endif\r
+\r
+#if !defined(XXH_USE_UNALIGNED_ACCESS)\r
+    if ((((U32)p) & 3) == 0) return XXH32_alignedInput(input, len, seed);   // Input is aligned, let's leverage the speed advantage\r
+#endif\r
+\r
+    if (len>=16)\r
+    {\r
+        const BYTE* const limit = bEnd - 16;\r
+        U32 v1 = seed + PRIME32_1 + PRIME32_2;\r
+        U32 v2 = seed + PRIME32_2;\r
+        U32 v3 = seed + 0;\r
+        U32 v4 = seed - PRIME32_1;\r
+\r
+        do\r
+        {\r
+            v1 += XXH_LE32(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;\r
+            v2 += XXH_LE32(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;\r
+            v3 += XXH_LE32(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;\r
+            v4 += XXH_LE32(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;\r
+        } while (p<=limit);\r
+\r
+        h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);\r
+    }\r
+    else\r
+    {\r
+        h32  = seed + PRIME32_5;\r
+    }\r
+\r
+    h32 += (U32) len;\r
+\r
+    while (p<=bEnd-4)\r
+    {\r
+        h32 += XXH_LE32(p) * PRIME32_3;\r
+        h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;\r
+        p+=4;\r
+    }\r
+\r
+    while (p<bEnd)\r
+    {\r
+        h32 += (*p) * PRIME32_5;\r
+        h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;\r
+        p++;\r
+    }\r
+\r
+    h32 ^= h32 >> 15;\r
+    h32 *= PRIME32_2;\r
+    h32 ^= h32 >> 13;\r
+    h32 *= PRIME32_3;\r
+    h32 ^= h32 >> 16;\r
+\r
+    return h32;\r
+\r
+#endif\r
+}\r
+\r
+\r
+//****************************\r
+// Advanced Hash Functions\r
+//****************************\r
+\r
+struct XXH_state32_t\r
+{\r
+    U64 total_len;\r
+    U32 seed;\r
+    U32 v1;\r
+    U32 v2;\r
+    U32 v3;\r
+    U32 v4;\r
+    int memsize;\r
+    char memory[16];\r
+};\r
+\r
+\r
+int XXH32_sizeofState() \r
+{\r
+    XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t));   // A compilation error here means XXH32_SIZEOFSTATE is not large enough\r
+    return sizeof(struct XXH_state32_t); \r
+}\r
+\r
+\r
+XXH_errorcode XXH32_resetState(void* state_in, U32 seed)\r
+{ \r
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;\r
+    state->seed = seed;\r
+    state->v1 = seed + PRIME32_1 + PRIME32_2;\r
+    state->v2 = seed + PRIME32_2;\r
+    state->v3 = seed + 0;\r
+    state->v4 = seed - PRIME32_1;\r
+    state->total_len = 0;\r
+    state->memsize = 0;\r
+    return XXH_OK;\r
+}\r
+\r
+\r
+void* XXH32_init (U32 seed)\r
+{\r
+    void* state = XXH_malloc (sizeof(struct XXH_state32_t));\r
+    XXH32_resetState(state, seed);\r
+    return state;\r
+}\r
+\r
+\r
+XXH_errorcode XXH32_update (void* state_in, const void* input, int len)\r
+{\r
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;\r
+    const BYTE* p = (const BYTE*)input;\r
+    const BYTE* const bEnd = p + len;\r
+\r
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER\r
+    if (input==NULL) return XXH_ERROR;\r
+#endif\r
+\r
+    state->total_len += len;\r
+\r
+    if (state->memsize + len < 16)   // fill in tmp buffer\r
+    {\r
+        XXH_memcpy(state->memory + state->memsize, input, len);\r
+        state->memsize +=  len;\r
+        return XXH_OK;\r
+    }\r
+\r
+    if (state->memsize)   // some data left from previous update\r
+    {\r
+        XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize);\r
+        {\r
+            const U32* p32 = (const U32*)state->memory;\r
+            state->v1 += XXH_LE32(p32) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++;\r
+            state->v2 += XXH_LE32(p32) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++; \r
+            state->v3 += XXH_LE32(p32) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++;\r
+            state->v4 += XXH_LE32(p32) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++;\r
+        }\r
+        p += 16-state->memsize;\r
+        state->memsize = 0;\r
+    }\r
+\r
+    if (p <= bEnd-16)\r
+    {\r
+        const BYTE* const limit = bEnd - 16;\r
+        U32 v1 = state->v1;\r
+        U32 v2 = state->v2;\r
+        U32 v3 = state->v3;\r
+        U32 v4 = state->v4;\r
+\r
+        do\r
+        {\r
+            v1 += XXH_LE32(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;\r
+            v2 += XXH_LE32(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;\r
+            v3 += XXH_LE32(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;\r
+            v4 += XXH_LE32(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;\r
+        } while (p<=limit);\r
+\r
+        state->v1 = v1;\r
+        state->v2 = v2;\r
+        state->v3 = v3;\r
+        state->v4 = v4;\r
+    }\r
+\r
+    if (p < bEnd)\r
+    {\r
+        XXH_memcpy(state->memory, p, bEnd-p);\r
+        state->memsize = (int)(bEnd-p);\r
+    }\r
+\r
+    return XXH_OK;\r
+}\r
+\r
+\r
+U32 XXH32_intermediateDigest (void* state_in)\r
+{\r
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;\r
+    BYTE * p   = (BYTE*)state->memory;\r
+    BYTE* bEnd = (BYTE*)state->memory + state->memsize;\r
+    U32 h32;\r
+\r
+    if (state->total_len >= 16)\r
+    {\r
+        h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);\r
+    }\r
+    else\r
+    {\r
+        h32  = state->seed + PRIME32_5;\r
+    }\r
+\r
+    h32 += (U32) state->total_len;\r
+\r
+    while (p<=bEnd-4)\r
+    {\r
+        h32 += XXH_LE32(p) * PRIME32_3;\r
+        h32 = XXH_rotl32(h32, 17) * PRIME32_4;\r
+        p+=4;\r
+    }\r
+\r
+    while (p<bEnd)\r
+    {\r
+        h32 += (*p) * PRIME32_5;\r
+        h32 = XXH_rotl32(h32, 11) * PRIME32_1;\r
+        p++;\r
+    }\r
+\r
+    h32 ^= h32 >> 15;\r
+    h32 *= PRIME32_2;\r
+    h32 ^= h32 >> 13;\r
+    h32 *= PRIME32_3;\r
+    h32 ^= h32 >> 16;\r
+\r
+    return h32;\r
+}\r
+\r
+\r
+U32 XXH32_digest (void* state_in)\r
+{\r
+    U32 h32 = XXH32_intermediateDigest(state_in);\r
+\r
+    XXH_free(state_in);\r
+\r
+    return h32;\r
+}\r
diff --git a/silo/third-party/lz4/xxhash.h b/silo/third-party/lz4/xxhash.h
new file mode 100644 (file)
index 0000000..8cb06d3
--- /dev/null
@@ -0,0 +1,164 @@
+/*\r
+   xxHash - Fast Hash algorithm\r
+   Header File\r
+   Copyright (C) 2012-2013, Yann Collet.\r
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\r
+\r
+   Redistribution and use in source and binary forms, with or without\r
+   modification, are permitted provided that the following conditions are\r
+   met:\r
+  \r
+       * Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+       * Redistributions in binary form must reproduce the above\r
+   copyright notice, this list of conditions and the following disclaimer\r
+   in the documentation and/or other materials provided with the\r
+   distribution.\r
+  \r
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+   You can contact the author at :\r
+   - xxHash source repository : http://code.google.com/p/xxhash/\r
+*/\r
+\r
+/* Notice extracted from xxHash homepage :\r
+\r
+xxHash is an extremely fast Hash algorithm, running at RAM speed limits.\r
+It also successfully passes all tests from the SMHasher suite.\r
+\r
+Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)\r
+\r
+Name            Speed       Q.Score   Author\r
+xxHash          5.4 GB/s     10\r
+CrapWow         3.2 GB/s      2       Andrew\r
+MumurHash 3a    2.7 GB/s     10       Austin Appleby\r
+SpookyHash      2.0 GB/s     10       Bob Jenkins\r
+SBox            1.4 GB/s      9       Bret Mulvey\r
+Lookup3         1.2 GB/s      9       Bob Jenkins\r
+SuperFastHash   1.2 GB/s      1       Paul Hsieh\r
+CityHash64      1.05 GB/s    10       Pike & Alakuijala\r
+FNV             0.55 GB/s     5       Fowler, Noll, Vo\r
+CRC32           0.43 GB/s     9\r
+MD5-32          0.33 GB/s    10       Ronald L. Rivest\r
+SHA1-32         0.28 GB/s    10\r
+\r
+Q.Score is a measure of quality of the hash function. \r
+It depends on successfully passing SMHasher test set. \r
+10 is a perfect score.\r
+*/\r
+\r
+#pragma once\r
+\r
+#if defined (__cplusplus)\r
+extern "C" {\r
+#endif\r
+\r
+\r
+//****************************\r
+// Type\r
+//****************************\r
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;\r
+\r
+\r
+\r
+//****************************\r
+// Simple Hash Functions\r
+//****************************\r
+\r
+unsigned int XXH32 (const void* input, int len, unsigned int seed);\r
+\r
+/*\r
+XXH32() :\r
+    Calculate the 32-bits hash of sequence of length "len" stored at memory address "input".\r
+    The memory between input & input+len must be valid (allocated and read-accessible).\r
+    "seed" can be used to alter the result predictably.\r
+    This function successfully passes all SMHasher tests.\r
+    Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s\r
+    Note that "len" is type "int", which means it is limited to 2^31-1.\r
+    If your data is larger, use the advanced functions below.\r
+*/\r
+\r
+\r
+\r
+//****************************\r
+// Advanced Hash Functions\r
+//****************************\r
+\r
+void*         XXH32_init   (unsigned int seed);\r
+XXH_errorcode XXH32_update (void* state, const void* input, int len);\r
+unsigned int  XXH32_digest (void* state);\r
+\r
+/*\r
+These functions calculate the xxhash of an input provided in several small packets,\r
+as opposed to an input provided as a single block.\r
+\r
+It must be started with :\r
+void* XXH32_init()\r
+The function returns a pointer which holds the state of calculation.\r
+\r
+This pointer must be provided as "void* state" parameter for XXH32_update().\r
+XXH32_update() can be called as many times as necessary.\r
+The user must provide a valid (allocated) input.\r
+The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.\r
+Note that "len" is type "int", which means it is limited to 2^31-1. \r
+If your data is larger, it is recommended to chunk your data into blocks \r
+of size for example 2^30 (1GB) to avoid any "int" overflow issue.\r
+\r
+Finally, you can end the calculation anytime, by using XXH32_digest().\r
+This function returns the final 32-bits hash.\r
+You must provide the same "void* state" parameter created by XXH32_init().\r
+Memory will be freed by XXH32_digest().\r
+*/\r
+\r
+\r
+int           XXH32_sizeofState();\r
+XXH_errorcode XXH32_resetState(void* state, unsigned int seed);\r
+\r
+#define       XXH32_SIZEOFSTATE 48\r
+typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t;\r
+/*\r
+These functions allow user application to make its own allocation for state.\r
+\r
+XXH32_sizeofState() is used to know how much space must be allocated for the xxHash 32-bits state.\r
+Note that the state must be aligned to access 'long long' fields. Memory must be allocated and referenced by a pointer.\r
+This pointer must then be provided as 'state' into XXH32_resetState(), which initializes the state.\r
+\r
+For static allocation purposes (such as allocation on stack, or freestanding systems without malloc()),\r
+use the structure XXH32_stateSpace_t, which will ensure that memory space is large enough and correctly aligned to access 'long long' fields.\r
+*/\r
+\r
+\r
+unsigned int XXH32_intermediateDigest (void* state);\r
+/*\r
+This function does the same as XXH32_digest(), generating a 32-bit hash,\r
+but preserve memory context.\r
+This way, it becomes possible to generate intermediate hashes, and then continue feeding data with XXH32_update().\r
+To free memory context, use XXH32_digest(), or free().\r
+*/\r
+\r
+\r
+\r
+//****************************\r
+// Deprecated function names\r
+//****************************\r
+// The following translations are provided to ease code transition\r
+// You are encouraged to no longer this function names\r
+#define XXH32_feed   XXH32_update\r
+#define XXH32_result XXH32_digest\r
+#define XXH32_getIntermediateResult XXH32_intermediateDigest\r
+\r
+\r
+\r
+#if defined (__cplusplus)\r
+}\r
+#endif\r
diff --git a/silo/third-party/lz4/xxhash.o b/silo/third-party/lz4/xxhash.o
new file mode 100644 (file)
index 0000000..a5e68b9
Binary files /dev/null and b/silo/third-party/lz4/xxhash.o differ
diff --git a/silo/thread.cc b/silo/thread.cc
new file mode 100644 (file)
index 0000000..d9f5bd7
--- /dev/null
@@ -0,0 +1,31 @@
+#include "macros.h"
+#include "thread.h"
+
+using namespace std;
+
+ndb_thread::~ndb_thread()
+{
+}
+
+void
+ndb_thread::start()
+{
+  thd_ = std::move(thread(&ndb_thread::run, this));
+  if (daemon_)
+    thd_.detach();
+}
+
+void
+ndb_thread::join()
+{
+  ALWAYS_ASSERT(!daemon_);
+  thd_.join();
+}
+
+// can be overloaded by subclasses
+void
+ndb_thread::run()
+{
+  ALWAYS_ASSERT(body_);
+  body_();
+}
diff --git a/silo/thread.h b/silo/thread.h
new file mode 100644 (file)
index 0000000..476aecf
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _NDB_THREAD_H_
+#define _NDB_THREAD_H_
+
+#include <vector>
+#include <string>
+#include <thread>
+
+#include "macros.h"
+
+/**
+ * WARNING: This class is DEPRECATED. New code should use std::thread directly
+ *
+ * ndb_thread: threads in NuDB
+ *
+ * Note that ndb_threads are thin wrappers around std::thread.
+ *
+ * There is really no point to use this-- in the past we used this to grab
+ * hooks into threads when they exited. This is no longer necessary, so we
+ * removed the hook code and this exists just for legacy reasons.
+ */
+
+class ndb_thread {
+public:
+
+  typedef void (*run_t)(void);
+
+  ndb_thread(bool daemon = false, const std::string &name = "thd")
+    : body_(nullptr), daemon_(daemon), name_(name) {}
+  ndb_thread(run_t body, bool daemon = false, const std::string &name = "thd")
+    : body_(body), daemon_(daemon), name_(name) {}
+
+  ndb_thread(const ndb_thread &) = delete;
+  ndb_thread(ndb_thread &&) = delete;
+  ndb_thread &operator=(const ndb_thread &) = delete;
+
+  virtual ~ndb_thread();
+
+  inline const std::string &
+  get_name() const
+  {
+    return name_;
+  }
+
+  void start();
+  void join();
+  virtual void run();
+
+private:
+  run_t body_;
+  std::thread thd_;
+  const bool daemon_;
+  const std::string name_;
+};
+
+#endif /* _NDB_THREAD_H_ */
diff --git a/silo/ticker.cc b/silo/ticker.cc
new file mode 100644 (file)
index 0000000..432eab4
--- /dev/null
@@ -0,0 +1,3 @@
+#include "ticker.h"
+
+ticker ticker::s_instance;
diff --git a/silo/ticker.h b/silo/ticker.h
new file mode 100644 (file)
index 0000000..8277fd1
--- /dev/null
@@ -0,0 +1,241 @@
+#pragma once
+
+#include <cstdint>
+#include <atomic>
+#include <thread>
+
+#include "core.h"
+#include "macros.h"
+#include "spinlock.h"
+#include "lockguard.h"
+
+class ticker {
+public:
+
+#ifdef CHECK_INVARIANTS
+  static const uint64_t tick_us = 1 * 1000; /* 1 ms */
+#else
+  static const uint64_t tick_us = 40 * 1000; /* 40 ms */
+#endif
+
+  ticker()
+    : current_tick_(1), last_tick_inclusive_(0)
+  {
+    std::thread thd(&ticker::tickerloop, this);
+    thd.detach();
+  }
+
+  inline uint64_t
+  global_current_tick() const
+  {
+    return current_tick_.load(std::memory_order_acquire);
+  }
+
+  inline uint64_t
+  global_last_tick_inclusive() const
+  {
+    return last_tick_inclusive_.load(std::memory_order_acquire);
+  }
+
+  inline uint64_t
+  global_last_tick_exclusive() const
+  {
+    return global_last_tick_inclusive() + 1;
+  }
+
+  // should yield a # >= global_last_tick_exclusive()
+  uint64_t
+  compute_global_last_tick_exclusive() const
+  {
+    uint64_t e = ticks_[0].current_tick_.load(std::memory_order_acquire);
+    for (size_t i = 1; i < ticks_.size(); i++)
+      e = std::min(e, ticks_[i].current_tick_.load(std::memory_order_acquire));
+    return e;
+  }
+
+  // returns true if guard is currently active, along with filling
+  // cur_epoch out
+  inline bool
+  is_locally_guarded(uint64_t &cur_epoch) const
+  {
+    const uint64_t core_id = coreid::core_id();
+    const uint64_t current_tick =
+      ticks_[core_id].current_tick_.load(std::memory_order_acquire);
+    const uint64_t current_depth =
+      ticks_[core_id].depth_.load(std::memory_order_acquire);
+    if (current_depth)
+      cur_epoch = current_tick;
+    return current_depth;
+  }
+
+  inline bool
+  is_locally_guarded() const
+  {
+    uint64_t c;
+    return is_locally_guarded(c);
+  }
+
+  inline spinlock &
+  lock_for(uint64_t core_id)
+  {
+    INVARIANT(core_id < ticks_.size());
+    return ticks_[core_id].lock_;
+  }
+
+  // a guard is re-entrant within a single thread
+  class guard {
+  public:
+
+    guard(ticker &impl)
+      : impl_(&impl), core_(coreid::core_id()), start_us_(0)
+    {
+      tickinfo &ti = impl_->ticks_[core_];
+      // bump the depth first
+      const uint64_t prev_depth = util::non_atomic_fetch_add(ti.depth_, 1UL);
+      // grab the lock
+      if (!prev_depth) {
+        ti.lock_.lock();
+        // read epoch # (try to advance forward)
+        tick_ = impl_->global_current_tick();
+        INVARIANT(ti.current_tick_.load(std::memory_order_acquire) <= tick_);
+        ti.current_tick_.store(tick_, std::memory_order_release);
+        start_us_ = util::timer::cur_usec();
+        ti.start_us_.store(start_us_, std::memory_order_release);
+      } else {
+        tick_ = ti.current_tick_.load(std::memory_order_acquire);
+        start_us_ = ti.start_us_.load(std::memory_order_acquire);
+      }
+      INVARIANT(ti.lock_.is_locked());
+      depth_ = prev_depth + 1;
+    }
+
+    guard(guard &&) = default;
+    guard(const guard &) = delete;
+    guard &operator=(const guard &) = delete;
+
+    ~guard()
+    {
+      if (!impl_)
+        return;
+      INVARIANT(core_ == coreid::core_id());
+      tickinfo &ti = impl_->ticks_[core_];
+      INVARIANT(ti.lock_.is_locked());
+      INVARIANT(tick_ > impl_->global_last_tick_inclusive());
+      const uint64_t prev_depth = util::non_atomic_fetch_sub(ti.depth_, 1UL);
+      INVARIANT(prev_depth);
+      // unlock
+      if (prev_depth == 1) {
+        ti.start_us_.store(0, std::memory_order_release);
+        ti.lock_.unlock();
+      }
+    }
+
+    inline uint64_t
+    tick() const
+    {
+      INVARIANT(impl_);
+      return tick_;
+    }
+
+    inline uint64_t
+    core() const
+    {
+      INVARIANT(impl_);
+      return core_;
+    }
+
+    inline uint64_t
+    depth() const
+    {
+      INVARIANT(impl_);
+      return depth_;
+    }
+
+    inline const ticker &
+    impl() const
+    {
+      INVARIANT(impl_);
+      return *impl_;
+    }
+
+    // refers to the start time of the *outermost* scope
+    inline uint64_t
+    start_us() const
+    {
+      return start_us_;
+    }
+
+  private:
+    ticker *impl_;
+    uint64_t core_;
+    uint64_t tick_;
+    uint64_t depth_;
+    uint64_t start_us_;
+  };
+
+  static ticker s_instance CACHE_ALIGNED; // system wide ticker
+
+private:
+
+  void
+  tickerloop()
+  {
+    // runs as daemon
+    util::timer loop_timer;
+    struct timespec t;
+    for (;;) {
+
+      const uint64_t last_loop_usec = loop_timer.lap();
+      const uint64_t delay_time_usec = tick_us;
+      if (last_loop_usec < delay_time_usec) {
+        const uint64_t sleep_ns = (delay_time_usec - last_loop_usec) * 1000;
+        t.tv_sec  = sleep_ns / ONE_SECOND_NS;
+        t.tv_nsec = sleep_ns % ONE_SECOND_NS;
+        nanosleep(&t, nullptr);
+        loop_timer.lap(); // since we slept away the lag
+      }
+
+      // bump the current tick
+      // XXX: ignore overflow
+      const uint64_t last_tick = util::non_atomic_fetch_add(current_tick_, 1UL);
+      const uint64_t cur_tick  = last_tick + 1;
+
+      // wait for all threads to finish the last tick
+      for (size_t i = 0; i < ticks_.size(); i++) {
+        tickinfo &ti = ticks_[i];
+        const uint64_t thread_cur_tick =
+          ti.current_tick_.load(std::memory_order_acquire);
+        INVARIANT(thread_cur_tick == last_tick ||
+                  thread_cur_tick == cur_tick);
+        if (thread_cur_tick == cur_tick)
+          continue;
+        lock_guard<spinlock> lg(ti.lock_);
+        ti.current_tick_.store(cur_tick, std::memory_order_release);
+      }
+
+      last_tick_inclusive_.store(last_tick, std::memory_order_release);
+    }
+  }
+
+  struct tickinfo {
+    spinlock lock_; // guards current_tick_ and depth_
+
+    std::atomic<uint64_t> current_tick_; // last RCU epoch this thread has seen
+                                         // (implies completion through current_tick_ - 1)
+    std::atomic<uint64_t> depth_; // 0 if not in RCU section
+    std::atomic<uint64_t> start_us_; // 0 if not in RCU section
+
+    tickinfo()
+      : current_tick_(1), depth_(0), start_us_(0)
+    {
+      ALWAYS_ASSERT(((uintptr_t)this % CACHELINE_SIZE) == 0);
+    }
+  };
+
+  percore<tickinfo> ticks_;
+
+  std::atomic<uint64_t> current_tick_; // which tick are we currenlty on?
+  std::atomic<uint64_t> last_tick_inclusive_;
+    // all threads have *completed* ticks <= last_tick_inclusive_
+    // (< current_tick_)
+};
diff --git a/silo/tuple.cc b/silo/tuple.cc
new file mode 100644 (file)
index 0000000..3731d9c
--- /dev/null
@@ -0,0 +1,105 @@
+#include "tuple.h"
+#include "txn.h"
+
+using namespace std;
+using namespace util;
+
+event_avg_counter dbtuple::g_evt_avg_dbtuple_stable_version_spins
+  ("avg_dbtuple_stable_version_spins");
+event_avg_counter dbtuple::g_evt_avg_dbtuple_lock_acquire_spins
+  ("avg_dbtuple_lock_acquire_spins");
+event_avg_counter dbtuple::g_evt_avg_dbtuple_read_retries
+  ("avg_dbtuple_read_retries");
+
+event_counter dbtuple::g_evt_dbtuple_creates("dbtuple_creates");
+event_counter dbtuple::g_evt_dbtuple_logical_deletes("dbtuple_logical_deletes");
+event_counter dbtuple::g_evt_dbtuple_physical_deletes("dbtuple_physical_deletes");
+event_counter dbtuple::g_evt_dbtuple_bytes_allocated("dbtuple_bytes_allocated");
+event_counter dbtuple::g_evt_dbtuple_bytes_freed("dbtuple_bytes_freed");
+event_counter dbtuple::g_evt_dbtuple_spills("dbtuple_spills");
+event_counter dbtuple::g_evt_dbtuple_inplace_buf_insufficient("dbtuple_inplace_buf_insufficient");
+event_counter dbtuple::g_evt_dbtuple_inplace_buf_insufficient_on_spill("dbtuple_inplace_buf_insufficient_on_spill");
+
+event_avg_counter dbtuple::g_evt_avg_record_spill_len("avg_record_spill_len");
+static event_avg_counter evt_avg_dbtuple_chain_length("avg_dbtuple_chain_len");
+
+dbtuple::~dbtuple()
+{
+  CheckMagic();
+  INVARIANT(!is_locked());
+  INVARIANT(!is_latest());
+  INVARIANT(!is_write_intent());
+  INVARIANT(!is_modifying());
+
+  VERBOSE(cerr << "dbtuple: " << hexify(intptr_t(this)) << " is being deleted" << endl);
+
+  // only free this instance
+
+  // stats-keeping
+  ++g_evt_dbtuple_physical_deletes;
+  g_evt_dbtuple_bytes_freed += (alloc_size + sizeof(dbtuple));
+
+}
+
+void
+dbtuple::gc_this()
+{
+  INVARIANT(rcu::s_instance.in_rcu_region());
+  INVARIANT(!is_latest());
+  release(this);
+}
+
+string
+dbtuple::VersionInfoStr(version_t v)
+{
+  ostringstream buf;
+  buf << "[";
+  buf << (IsLocked(v) ? "LOCKED" : "-") << " | ";
+  buf << (IsDeleting(v) ? "DEL" : "-") << " | ";
+  buf << (IsWriteIntent(v) ? "WR" : "-") << " | ";
+  buf << (IsModifying(v) ? "MOD" : "-") << " | ";
+  buf << (IsLatest(v) ? "LATEST" : "-") << " | ";
+  buf << Version(v);
+  buf << "]";
+  return buf.str();
+}
+
+static ostream &
+format_tuple(ostream &o, const dbtuple &t)
+{
+  string truncated_contents(
+      (const char *) &t.value_start[0], min(static_cast<size_t>(t.size), 16UL));
+  o << &t << " [tid=" << g_proto_version_str(t.version)
+    << ", size=" << t.size
+    << ", contents=0x" << hexify(truncated_contents) << (t.size > 16 ? "..." : "")
+    << ", next=" << t.next << "]";
+  return o;
+}
+
+void
+dbtuple::print(ostream &o, unsigned len) const
+{
+  o << "dbtuple:" << endl
+    << "  hdr=" << VersionInfoStr(unstable_version())
+#ifdef TUPLE_CHECK_KEY
+    << endl << "  key=" << hexify(key)
+    << endl << "  tree=" << tree
+#endif
+    << endl;
+
+  size_t n = 0;
+  for (const dbtuple *p = this;
+       p && n < len;
+       p = p->get_next(), ++n) {
+    o << "  ";
+    format_tuple(o, *p);
+    o << endl;
+  }
+}
+
+ostream &
+operator<<(ostream &o, const dbtuple &t)
+{
+  t.print(o, 1);
+  return o;
+}
diff --git a/silo/tuple.h b/silo/tuple.h
new file mode 100644 (file)
index 0000000..5817923
--- /dev/null
@@ -0,0 +1,1099 @@
+#ifndef _NDB_TUPLE_H_
+#define _NDB_TUPLE_H_
+
+#include <atomic>
+#include <vector>
+#include <string>
+#include <utility>
+#include <limits>
+#include <unordered_map>
+#include <ostream>
+#include <thread>
+
+#include "amd64.h"
+#include "core.h"
+#include "counter.h"
+#include "macros.h"
+#include "varkey.h"
+#include "util.h"
+#include "rcu.h"
+#include "thread.h"
+#include "spinlock.h"
+#include "small_unordered_map.h"
+#include "prefetch.h"
+#include "ownership_checker.h"
+
+// debugging tool
+//#define TUPLE_LOCK_OWNERSHIP_CHECKING
+
+template <template <typename> class Protocol, typename Traits>
+  class transaction; // forward decl
+
+// XXX: hack
+extern std::string (*g_proto_version_str)(uint64_t v);
+
+/**
+ * A dbtuple is the type of value which we stick
+ * into underlying (non-transactional) data structures- it
+ * also contains the memory of the value
+ */
+struct dbtuple {
+  friend std::ostream &
+  operator<<(std::ostream &o, const dbtuple &tuple);
+
+public:
+  // trying to save space by putting constraints
+  // on node maximums
+  typedef uint32_t version_t;
+  typedef uint16_t node_size_type;
+  typedef uint64_t tid_t;
+  typedef uint8_t * record_type;
+  typedef const uint8_t * const_record_type;
+  typedef size_t size_type;
+  typedef std::string string_type;
+
+  static const tid_t MIN_TID = 0;
+  static const tid_t MAX_TID = (tid_t) -1;
+
+  // lock ownership helpers- works by recording all tuple
+  // locks obtained in each transaction, and then when the txn
+  // finishes, calling AssertAllTupleLocksReleased(), which makes
+  // sure the current thread is no longer the owner of any locks
+  // acquired during the txn
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+  static inline void
+  TupleLockRegionBegin()
+  {
+    ownership_checker<dbtuple, dbtuple>::NodeLockRegionBegin();
+  }
+
+  // is used to signal the end of a tuple lock region
+  static inline void
+  AssertAllTupleLocksReleased()
+  {
+    ownership_checker<dbtuple, dbtuple>::AssertAllNodeLocksReleased();
+  }
+private:
+  static inline void
+  AddTupleToLockRegion(const dbtuple *n)
+  {
+    ownership_checker<dbtuple, dbtuple>::AddNodeToLockRegion(n);
+  }
+#endif
+
+private:
+  static const version_t HDR_LOCKED_MASK = 0x1;
+
+  static const version_t HDR_DELETING_SHIFT = 1;
+  static const version_t HDR_DELETING_MASK = 0x1 << HDR_DELETING_SHIFT;
+
+  static const version_t HDR_WRITE_INTENT_SHIFT = 2;
+  static const version_t HDR_WRITE_INTENT_MASK = 0x1 << HDR_WRITE_INTENT_SHIFT;
+
+  static const version_t HDR_MODIFYING_SHIFT = 3;
+  static const version_t HDR_MODIFYING_MASK = 0x1 << HDR_MODIFYING_SHIFT;
+
+  static const version_t HDR_LATEST_SHIFT = 4;
+  static const version_t HDR_LATEST_MASK = 0x1 << HDR_LATEST_SHIFT;
+
+  static const version_t HDR_VERSION_SHIFT = 5;
+  static const version_t HDR_VERSION_MASK = ((version_t)-1) << HDR_VERSION_SHIFT;
+
+public:
+
+#ifdef TUPLE_MAGIC
+  class magic_failed_exception: public std::exception {};
+  static const uint8_t TUPLE_MAGIC = 0x29U;
+  uint8_t magic;
+  inline ALWAYS_INLINE void CheckMagic() const
+  {
+    if (unlikely(magic != TUPLE_MAGIC)) {
+      print(1);
+      // so we can catch it later and print out useful debugging output
+      throw magic_failed_exception();
+    }
+  }
+#else
+  inline ALWAYS_INLINE void CheckMagic() const {}
+#endif
+
+  // NB(stephentu): ABA problem happens after some multiple of
+  // 2^(NBits(version_t)-6) concurrent modifications- somewhat low probability
+  // event, so we let it happen
+  //
+  // <-- low bits
+  // [ locked | deleting | write_intent | modifying | latest | version ]
+  // [  0..1  |   1..2   |    2..3      |   3..4    |  4..5  |  5..32  ]
+  volatile version_t hdr;
+
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+  std::thread::id lock_owner;
+#endif
+
+  // uninterpreted TID
+  tid_t version;
+
+  // small sizes on purpose
+  node_size_type size; // actual size of record
+                       // (only meaningful is the deleting bit is not set)
+  node_size_type alloc_size; // max size record allowed. is the space
+                             // available for the record buf
+
+  dbtuple *next; // be very careful about traversing this pointer,
+                 // GC is capable of reaping it at certain (well defined)
+                 // points, and will not bother to set it to null
+
+#ifdef TUPLE_CHECK_KEY
+  // for debugging
+  std::string key;
+  void *tree;
+#endif
+
+#ifdef CHECK_INVARIANTS
+  // for debugging
+  std::atomic<uint64_t> opaque;
+#endif
+
+  // must be last field
+  uint8_t value_start[0];
+
+  void print(std::ostream &o, unsigned len) const;
+
+private:
+  // private ctor/dtor b/c we do some special memory stuff
+  // ctors start node off as latest node
+
+  static inline ALWAYS_INLINE node_size_type
+  CheckBounds(size_type s)
+  {
+    INVARIANT(s <= std::numeric_limits<node_size_type>::max());
+    return s;
+  }
+
+  dbtuple(const dbtuple &) = delete;
+  dbtuple(dbtuple &&) = delete;
+  dbtuple &operator=(const dbtuple &) = delete;
+
+  // creates a (new) record with a tentative value at MAX_TID
+  dbtuple(size_type size, size_type alloc_size, bool acquire_lock)
+    :
+#ifdef TUPLE_MAGIC
+      magic(TUPLE_MAGIC),
+#endif
+      hdr(HDR_LATEST_MASK |
+          (acquire_lock ? (HDR_LOCKED_MASK | HDR_WRITE_INTENT_MASK) : 0) |
+          (!size ? HDR_DELETING_MASK : 0))
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+      , lock_owner()
+#endif
+      , version(MAX_TID)
+      , size(CheckBounds(size))
+      , alloc_size(CheckBounds(alloc_size))
+      , next(nullptr)
+#ifdef TUPLE_CHECK_KEY
+      , key()
+      , tree(nullptr)
+#endif
+#ifdef CHECK_INVARIANTS
+      , opaque(0)
+#endif
+  {
+    INVARIANT(((char *)this) + sizeof(*this) == (char *) &value_start[0]);
+    INVARIANT(is_latest());
+    INVARIANT(size || is_deleting());
+    ++g_evt_dbtuple_creates;
+    g_evt_dbtuple_bytes_allocated += alloc_size + sizeof(dbtuple);
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+    if (acquire_lock) {
+      lock_owner = std::this_thread::get_id();
+      AddTupleToLockRegion(this);
+      INVARIANT(is_lock_owner());
+    }
+#endif
+    COMPILER_MEMORY_FENCE; // for acquire_lock
+  }
+
+  // creates a record at version derived from base
+  // (inheriting its value).
+  dbtuple(tid_t version,
+          struct dbtuple *base,
+          size_type alloc_size,
+          bool set_latest)
+    :
+#ifdef TUPLE_MAGIC
+      magic(TUPLE_MAGIC),
+#endif
+      hdr(set_latest ? HDR_LATEST_MASK : 0)
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+      , lock_owner()
+#endif
+      , version(version)
+      , size(base->size)
+      , alloc_size(CheckBounds(alloc_size))
+      , next(base->next)
+#ifdef TUPLE_CHECK_KEY
+      , key()
+      , tree(nullptr)
+#endif
+#ifdef CHECK_INVARIANTS
+      , opaque(0)
+#endif
+  {
+    INVARIANT(size <= alloc_size);
+    INVARIANT(set_latest == is_latest());
+    if (base->is_deleting())
+      mark_deleting();
+    NDB_MEMCPY(&value_start[0], base->get_value_start(), size);
+    ++g_evt_dbtuple_creates;
+    g_evt_dbtuple_bytes_allocated += alloc_size + sizeof(dbtuple);
+  }
+
+  // creates a spill record, copying in the *old* value if necessary, but
+  // setting the size to the *new* value
+  dbtuple(tid_t version,
+          const_record_type r,
+          size_type old_size,
+          size_type new_size,
+          size_type alloc_size,
+          struct dbtuple *next,
+          bool set_latest,
+          bool needs_old_value)
+    :
+#ifdef TUPLE_MAGIC
+      magic(TUPLE_MAGIC),
+#endif
+      hdr((set_latest ? HDR_LATEST_MASK : 0) | (!new_size ? HDR_DELETING_MASK : 0))
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+      , lock_owner()
+#endif
+      , version(version)
+      , size(CheckBounds(new_size))
+      , alloc_size(CheckBounds(alloc_size))
+      , next(next)
+#ifdef TUPLE_CHECK_KEY
+      , key()
+      , tree(nullptr)
+#endif
+#ifdef CHECK_INVARIANTS
+      , opaque(0)
+#endif
+  {
+    INVARIANT(!needs_old_value || old_size <= alloc_size);
+    INVARIANT(new_size <= alloc_size);
+    INVARIANT(set_latest == is_latest());
+    INVARIANT(new_size || is_deleting());
+    if (needs_old_value)
+      NDB_MEMCPY(&value_start[0], r, old_size);
+    ++g_evt_dbtuple_creates;
+    g_evt_dbtuple_bytes_allocated += alloc_size + sizeof(dbtuple);
+  }
+
+  friend class rcu;
+  ~dbtuple();
+
+  static event_avg_counter g_evt_avg_dbtuple_stable_version_spins;
+  static event_avg_counter g_evt_avg_dbtuple_lock_acquire_spins;
+  static event_avg_counter g_evt_avg_dbtuple_read_retries;
+
+public:
+
+  enum ReadStatus {
+    READ_FAILED,
+    READ_EMPTY,
+    READ_RECORD,
+  };
+
+  inline void
+  prefetch() const
+  {
+#ifdef TUPLE_PREFETCH
+    prefetch_bytes(this, sizeof(*this) + alloc_size);
+#endif
+  }
+
+  // gcs *this* instance, ignoring the chain
+  void gc_this();
+
+  inline bool
+  is_locked() const
+  {
+    return IsLocked(hdr);
+  }
+
+  static inline bool
+  IsLocked(version_t v)
+  {
+    return v & HDR_LOCKED_MASK;
+  }
+
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+  inline bool
+  is_lock_owner() const
+  {
+    return std::this_thread::get_id() == lock_owner;
+  }
+#else
+  inline bool
+  is_lock_owner() const
+  {
+    return true;
+  }
+#endif
+
+  inline version_t
+  lock(bool write_intent)
+  {
+    // XXX: implement SPINLOCK_BACKOFF
+    CheckMagic();
+#ifdef ENABLE_EVENT_COUNTERS
+    unsigned nspins = 0;
+#endif
+    version_t v = hdr;
+    const version_t lockmask = write_intent ?
+      (HDR_LOCKED_MASK | HDR_WRITE_INTENT_MASK) :
+      (HDR_LOCKED_MASK);
+    while (IsLocked(v) ||
+           !__sync_bool_compare_and_swap(&hdr, v, v | lockmask)) {
+      nop_pause();
+      v = hdr;
+#ifdef ENABLE_EVENT_COUNTERS
+      ++nspins;
+#endif
+    }
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+    lock_owner = std::this_thread::get_id();
+    AddTupleToLockRegion(this);
+    INVARIANT(is_lock_owner());
+#endif
+    COMPILER_MEMORY_FENCE;
+    INVARIANT(IsLocked(hdr));
+    INVARIANT(!write_intent || IsWriteIntent(hdr));
+    INVARIANT(!IsModifying(hdr));
+#ifdef ENABLE_EVENT_COUNTERS
+    g_evt_avg_dbtuple_lock_acquire_spins.offer(nspins);
+#endif
+    return hdr;
+  }
+
+  inline void
+  unlock()
+  {
+    CheckMagic();
+    version_t v = hdr;
+    bool newv = false;
+    INVARIANT(IsLocked(v));
+    INVARIANT(is_lock_owner());
+    if (IsModifying(v) || IsWriteIntent(v)) {
+      newv = true;
+      const version_t n = Version(v);
+      v &= ~HDR_VERSION_MASK;
+      v |= (((n + 1) << HDR_VERSION_SHIFT) & HDR_VERSION_MASK);
+    }
+    // clear locked + modifying bits
+    v &= ~(HDR_LOCKED_MASK | HDR_MODIFYING_MASK | HDR_WRITE_INTENT_MASK);
+    if (newv) {
+      INVARIANT(!reader_check_version(v));
+      INVARIANT(!writer_check_version(v));
+    }
+    INVARIANT(!IsLocked(v));
+    INVARIANT(!IsModifying(v));
+    INVARIANT(!IsWriteIntent(v));
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+    lock_owner = std::thread::id();
+    INVARIANT(!is_lock_owner());
+#endif
+    COMPILER_MEMORY_FENCE;
+    hdr = v;
+  }
+
+  inline bool
+  is_deleting() const
+  {
+    return IsDeleting(hdr);
+  }
+
+  static inline bool
+  IsDeleting(version_t v)
+  {
+    return v & HDR_DELETING_MASK;
+  }
+
+  inline void
+  mark_deleting()
+  {
+    CheckMagic();
+    // the lock on the latest version guards non-latest versions
+    INVARIANT(!is_latest() || is_locked());
+    INVARIANT(!is_latest() || is_lock_owner());
+    INVARIANT(!is_deleting());
+    hdr |= HDR_DELETING_MASK;
+  }
+
+  inline void
+  clear_deleting()
+  {
+    CheckMagic();
+    INVARIANT(is_locked());
+    INVARIANT(is_lock_owner());
+    INVARIANT(is_deleting());
+    hdr &= ~HDR_DELETING_MASK;
+  }
+
+  inline bool
+  is_modifying() const
+  {
+    return IsModifying(hdr);
+  }
+
+  inline void
+  mark_modifying()
+  {
+    CheckMagic();
+    version_t v = hdr;
+    INVARIANT(IsLocked(v));
+    INVARIANT(is_lock_owner());
+    //INVARIANT(!IsModifying(v)); // mark_modifying() must be re-entrant
+    v |= HDR_MODIFYING_MASK;
+    COMPILER_MEMORY_FENCE; // XXX: is this fence necessary?
+    hdr = v;
+    COMPILER_MEMORY_FENCE;
+  }
+
+  static inline bool
+  IsModifying(version_t v)
+  {
+    return v & HDR_MODIFYING_MASK;
+  }
+
+  inline bool
+  is_write_intent() const
+  {
+    return IsWriteIntent(hdr);
+  }
+
+  static inline bool
+  IsWriteIntent(version_t v)
+  {
+    return v & HDR_WRITE_INTENT_MASK;
+  }
+
+  inline bool
+  is_latest() const
+  {
+    return IsLatest(hdr);
+  }
+
+  static inline bool
+  IsLatest(version_t v)
+  {
+    return v & HDR_LATEST_MASK;
+  }
+
+  inline void
+  clear_latest()
+  {
+    CheckMagic();
+    INVARIANT(is_locked());
+    INVARIANT(is_lock_owner());
+    INVARIANT(is_latest());
+    hdr &= ~HDR_LATEST_MASK;
+  }
+
+  static inline version_t
+  Version(version_t v)
+  {
+    return (v & HDR_VERSION_MASK) >> HDR_VERSION_SHIFT;
+  }
+
+  inline version_t
+  reader_stable_version(bool allow_write_intent) const
+  {
+    version_t v = hdr;
+#ifdef ENABLE_EVENT_COUNTERS
+    unsigned long nspins = 0;
+#endif
+    while (IsModifying(v) ||
+           (!allow_write_intent && IsWriteIntent(v))) {
+      nop_pause();
+      v = hdr;
+#ifdef ENABLE_EVENT_COUNTERS
+      ++nspins;
+#endif
+    }
+    COMPILER_MEMORY_FENCE;
+#ifdef ENABLE_EVENT_COUNTERS
+    g_evt_avg_dbtuple_stable_version_spins.offer(nspins);
+#endif
+    return v;
+  }
+
+  /**
+   * returns true if succeeded, false otherwise
+   */
+  inline bool
+  try_writer_stable_version(version_t &v, unsigned int spins) const
+  {
+    v = hdr;
+    while (IsWriteIntent(v) && spins--) {
+      INVARIANT(IsLocked(v));
+      nop_pause();
+      v = hdr;
+    }
+    const bool ret = !IsWriteIntent(v);
+    COMPILER_MEMORY_FENCE;
+    INVARIANT(ret || IsLocked(v));
+    INVARIANT(!ret || !IsModifying(v));
+    return ret;
+  }
+
+  inline version_t
+  unstable_version() const
+  {
+    return hdr;
+  }
+
+  inline bool
+  reader_check_version(version_t version) const
+  {
+    COMPILER_MEMORY_FENCE;
+    // are the versions the same, modulo the
+    // {locked, write_intent, latest} bits?
+    const version_t MODULO_BITS =
+      (HDR_LOCKED_MASK | HDR_WRITE_INTENT_MASK | HDR_LATEST_MASK);
+    return (hdr & ~MODULO_BITS) == (version & ~MODULO_BITS);
+  }
+
+  inline bool
+  writer_check_version(version_t version) const
+  {
+    COMPILER_MEMORY_FENCE;
+    return hdr == version;
+  }
+
+  inline ALWAYS_INLINE struct dbtuple *
+  get_next()
+  {
+    return next;
+  }
+
+  inline const struct dbtuple *
+  get_next() const
+  {
+    return next;
+  }
+
+  inline ALWAYS_INLINE void
+  set_next(struct dbtuple *next)
+  {
+    CheckMagic();
+    this->next = next;
+  }
+
+  inline void
+  clear_next()
+  {
+    CheckMagic();
+    this->next = nullptr;
+  }
+
+  inline ALWAYS_INLINE uint8_t *
+  get_value_start()
+  {
+    CheckMagic();
+    return &value_start[0];
+  }
+
+  inline ALWAYS_INLINE const uint8_t *
+  get_value_start() const
+  {
+    return &value_start[0];
+  }
+
+  // worst name ever...
+  inline bool
+  is_not_behind(tid_t t) const
+  {
+    return version <= t;
+  }
+
+private:
+
+#ifdef ENABLE_EVENT_COUNTERS
+  struct scoped_recorder {
+    scoped_recorder(unsigned long &n) : n(&n) {}
+    ~scoped_recorder()
+    {
+      g_evt_avg_dbtuple_read_retries.offer(*n);
+    }
+  private:
+    unsigned long *n;
+  };
+#endif
+
+  // written to be non-recursive
+  template <typename Reader, typename StringAllocator>
+  static ReadStatus
+  record_at_chain(
+      const dbtuple *starting, tid_t t, tid_t &start_t,
+      Reader &reader, StringAllocator &sa, bool allow_write_intent)
+  {
+#ifdef ENABLE_EVENT_COUNTERS
+    unsigned long nretries = 0;
+    scoped_recorder rec(nretries);
+#endif
+    const dbtuple *current = starting;
+  loop:
+    INVARIANT(current->version != MAX_TID);
+    const version_t v = current->reader_stable_version(allow_write_intent);
+    const struct dbtuple *p;
+    const bool found = current->is_not_behind(t);
+    if (found) {
+      start_t = current->version;
+      const size_t read_sz = IsDeleting(v) ? 0 : current->size;
+      if (unlikely(read_sz && !reader(current->get_value_start(), read_sz, sa)))
+        goto retry;
+      if (unlikely(!current->reader_check_version(v)))
+        goto retry;
+      return read_sz ? READ_RECORD : READ_EMPTY;
+    } else {
+      p = current->get_next();
+    }
+    if (unlikely(!current->reader_check_version(v)))
+      goto retry;
+    if (p) {
+      current = p;
+      goto loop;
+    }
+    // see note in record_at()
+    start_t = MIN_TID;
+    return READ_EMPTY;
+  retry:
+#ifdef ENABLE_EVENT_COUNTERS
+    ++nretries;
+#endif
+    goto loop;
+  }
+
+  // we force one level of inlining, but don't force record_at_chain()
+  // to be inlined
+  template <typename Reader, typename StringAllocator>
+  inline ALWAYS_INLINE ReadStatus
+  record_at(
+      tid_t t, tid_t &start_t,
+      Reader &reader, StringAllocator &sa, bool allow_write_intent) const
+  {
+#ifdef ENABLE_EVENT_COUNTERS
+    unsigned long nretries = 0;
+    scoped_recorder rec(nretries);
+#endif
+    if (unlikely(version == MAX_TID)) {
+      // XXX(stephentu): HACK! we use MAX_TID to indicate a tentative
+      // "insert"- the actual latest value is empty.
+      //
+      // since our system is screwed anyways if we ever reach MAX_TID, this
+      // is OK for now, but a real solution should exist at some point
+      start_t = MIN_TID;
+      return READ_EMPTY;
+    }
+  loop:
+    const version_t v = reader_stable_version(allow_write_intent);
+    const struct dbtuple *p;
+    const bool found = is_not_behind(t);
+    if (found) {
+      //if (unlikely(!IsLatest(v)))
+      //  return READ_FAILED;
+      start_t = version;
+      const size_t read_sz = IsDeleting(v) ? 0 : size;
+      if (unlikely(read_sz && !reader(get_value_start(), read_sz, sa)))
+        goto retry;
+      if (unlikely(!reader_check_version(v)))
+        goto retry;
+      return read_sz ? READ_RECORD : READ_EMPTY;
+    } else {
+      p = get_next();
+    }
+    if (unlikely(!reader_check_version(v)))
+      goto retry;
+    if (p)
+      return record_at_chain(p, t, start_t, reader, sa, allow_write_intent);
+    // NB(stephentu): if we reach the end of a chain then we assume that
+    // the record exists as a deleted record.
+    //
+    // This is safe because we have been very careful to not garbage collect
+    // elements along the chain until it is guaranteed that the record
+    // is superceded by later record in any consistent read. Therefore,
+    // if we reach the end of the chain, then it *must* be the case that
+    // the record does not actually exist.
+    //
+    // Note that MIN_TID is the *wrong* tid to use here given wrap-around- we
+    // really should be setting this value to the tid which represents the
+    // oldest TID possible in the system. But we currently don't implement
+    // wrap around
+    start_t = MIN_TID;
+    return READ_EMPTY;
+  retry:
+#ifdef ENABLE_EVENT_COUNTERS
+    ++nretries;
+#endif
+    goto loop;
+  }
+
+  static event_counter g_evt_dbtuple_creates;
+  static event_counter g_evt_dbtuple_logical_deletes;
+  static event_counter g_evt_dbtuple_physical_deletes;
+  static event_counter g_evt_dbtuple_bytes_allocated;
+  static event_counter g_evt_dbtuple_bytes_freed;
+  static event_counter g_evt_dbtuple_spills;
+  static event_counter g_evt_dbtuple_inplace_buf_insufficient;
+  static event_counter g_evt_dbtuple_inplace_buf_insufficient_on_spill;
+  static event_avg_counter g_evt_avg_record_spill_len;
+
+public:
+
+  /**
+   * Read the record at tid t. Returns true if such a record exists, false
+   * otherwise (ie the record was GC-ed, or other reasons). On a successful
+   * read, the value @ start_t will be stored in r
+   *
+   * NB(stephentu): calling stable_read() while holding the lock
+   * is an error- this will cause deadlock
+   */
+  template <typename Reader, typename StringAllocator>
+  inline ALWAYS_INLINE ReadStatus
+  stable_read(
+      tid_t t, tid_t &start_t,
+      Reader &reader, StringAllocator &sa,
+      bool allow_write_intent) const
+  {
+    return record_at(t, start_t, reader, sa, allow_write_intent);
+  }
+
+  inline bool
+  is_latest_version(tid_t t) const
+  {
+    return is_latest() && is_not_behind(t);
+  }
+
+  bool
+  stable_is_latest_version(tid_t t) const
+  {
+    version_t v = 0;
+    if (!try_writer_stable_version(v, 16))
+      return false;
+    // now v is a stable version
+    INVARIANT(!IsWriteIntent(v));
+    INVARIANT(!IsModifying(v));
+    const bool ret = IsLatest(v) && is_not_behind(t);
+    // only check_version() if the answer would be true- otherwise,
+    // no point in doing a version check
+    if (ret && writer_check_version(v))
+      return true;
+    else
+      // no point in retrying, since we know it will fail (since we had a
+      // version change)
+      return false;
+  }
+
+  inline bool
+  latest_value_is_nil() const
+  {
+    return is_latest() && size == 0;
+  }
+
+  inline bool
+  stable_latest_value_is_nil() const
+  {
+    version_t v = 0;
+    if (!try_writer_stable_version(v, 16))
+      return false;
+    INVARIANT(!IsWriteIntent(v));
+    INVARIANT(!IsModifying(v));
+    const bool ret = IsLatest(v) && size == 0;
+    if (ret && writer_check_version(v))
+      return true;
+    else
+      return false;
+  }
+
+  struct write_record_ret {
+    write_record_ret() : head_(), rest_(), forced_spill_() {}
+    write_record_ret(dbtuple *head, dbtuple* rest, bool forced_spill)
+      : head_(head), rest_(rest), forced_spill_(forced_spill)
+    {
+      INVARIANT(head);
+      INVARIANT(head != rest);
+      INVARIANT(!forced_spill || rest);
+    }
+    dbtuple *head_;
+    dbtuple *rest_;
+    bool forced_spill_;
+  };
+
+  // XXX: kind of hacky, but we do this to avoid virtual
+  // functions / passing multiple function pointers around
+  enum TupleWriterMode {
+    TUPLE_WRITER_NEEDS_OLD_VALUE, // all three args ignored
+    TUPLE_WRITER_COMPUTE_NEEDED,
+    TUPLE_WRITER_COMPUTE_DELTA_NEEDED, // last two args ignored
+    TUPLE_WRITER_DO_WRITE,
+    TUPLE_WRITER_DO_DELTA_WRITE,
+  };
+  typedef size_t (*tuple_writer_t)(TupleWriterMode, const void *, uint8_t *, size_t);
+
+  /**
+   * Always writes the record in the latest (newest) version slot,
+   * not asserting whether or not inserting r @ t would violate the
+   * sorted order invariant
+   *
+   * ret.first  = latest tuple after the write (guaranteed to not be nullptr)
+   * ret.second = old version of tuple, iff no overwrite (can be nullptr)
+   *
+   * Note: if this != ret.first, then we need a tree replacement
+   */
+  template <typename Transaction>
+  write_record_ret
+  write_record_at(const Transaction *txn, tid_t t,
+                  const void *v, tuple_writer_t writer)
+  {
+#ifndef DISABLE_OVERWRITE_IN_PLACE
+    CheckMagic();
+    INVARIANT(is_locked());
+    INVARIANT(is_lock_owner());
+    INVARIANT(is_latest());
+    INVARIANT(is_write_intent());
+
+    const size_t new_sz =
+      v ? writer(TUPLE_WRITER_COMPUTE_NEEDED, v, get_value_start(), size) : 0;
+    INVARIANT(!v || new_sz);
+    INVARIANT(is_deleting() || size);
+    const size_t old_sz = is_deleting() ? 0 : size;
+
+    if (!new_sz)
+      ++g_evt_dbtuple_logical_deletes;
+
+    // try to overwrite this record
+    if (likely(txn->can_overwrite_record_tid(version, t) && old_sz)) {
+      INVARIANT(!is_deleting());
+      // see if we have enough space
+      if (likely(new_sz <= alloc_size)) {
+        // directly update in place
+        mark_modifying();
+        if (v)
+          writer(TUPLE_WRITER_DO_WRITE, v, get_value_start(), old_sz);
+        version = t;
+        size = new_sz;
+        if (!new_sz)
+          mark_deleting();
+        return write_record_ret(this, nullptr, false);
+      }
+
+      //std::cerr
+      //  << "existing: " << g_proto_version_str(version) << std::endl
+      //  << "new     : " << g_proto_version_str(t)       << std::endl
+      //  << "alloc_size : " << alloc_size                << std::endl
+      //  << "new_sz     : " << new_sz                    << std::endl;
+
+      // keep this tuple in the chain (it's wasteful, but not incorrect)
+      // so that cleanup is easier
+      //
+      // XXX(stephentu): alloc_spill() should acquire the lock on
+      // the returned tuple in the ctor, as an optimization
+
+      const bool needs_old_value =
+        writer(TUPLE_WRITER_NEEDS_OLD_VALUE, nullptr, nullptr, 0);
+      INVARIANT(new_sz);
+      INVARIANT(v);
+      dbtuple * const rep =
+        alloc_spill(t, get_value_start(), old_sz, new_sz,
+                    this, true, needs_old_value);
+      writer(TUPLE_WRITER_DO_WRITE, v, rep->get_value_start(), old_sz);
+      INVARIANT(rep->is_latest());
+      INVARIANT(rep->size == new_sz);
+      clear_latest();
+      ++g_evt_dbtuple_inplace_buf_insufficient;
+
+      // [did not spill because of epochs, need to replace this with rep]
+      return write_record_ret(rep, this, false);
+    }
+
+    //std::cerr
+    //  << "existing: " << g_proto_version_str(version) << std::endl
+    //  << "new     : " << g_proto_version_str(t)       << std::endl
+    //  << "alloc_size : " << alloc_size                << std::endl
+    //  << "new_sz     : " << new_sz                    << std::endl;
+
+    // need to spill
+    ++g_evt_dbtuple_spills;
+    g_evt_avg_record_spill_len.offer(size);
+
+    if (new_sz <= alloc_size && old_sz) {
+      INVARIANT(!is_deleting());
+      dbtuple * const spill = alloc(version, this, false);
+      INVARIANT(!spill->is_latest());
+      mark_modifying();
+      set_next(spill);
+      if (v)
+        writer(TUPLE_WRITER_DO_WRITE, v, get_value_start(), size);
+      version = t;
+      size = new_sz;
+      if (!new_sz)
+        mark_deleting();
+      return write_record_ret(this, spill, true);
+    }
+
+    const bool needs_old_value =
+      writer(TUPLE_WRITER_NEEDS_OLD_VALUE, nullptr, nullptr, 0);
+    dbtuple * const rep =
+      alloc_spill(t, get_value_start(), old_sz, new_sz,
+                  this, true, needs_old_value);
+    if (v)
+      writer(TUPLE_WRITER_DO_WRITE, v, rep->get_value_start(), size);
+    INVARIANT(rep->is_latest());
+    INVARIANT(rep->size == new_sz);
+    INVARIANT(new_sz || rep->is_deleting()); // set by alloc_spill()
+    clear_latest();
+    ++g_evt_dbtuple_inplace_buf_insufficient_on_spill;
+    return write_record_ret(rep, this, true);
+#else
+    CheckMagic();
+    INVARIANT(is_locked());
+    INVARIANT(is_lock_owner());
+    INVARIANT(is_latest());
+    INVARIANT(is_write_intent());
+
+    const size_t new_sz =
+      v ? writer(TUPLE_WRITER_COMPUTE_NEEDED, v, get_value_start(), size) : 0;
+    INVARIANT(!v || new_sz);
+    INVARIANT(is_deleting() || size);
+    const size_t old_sz = is_deleting() ? 0 : size;
+
+    if (!new_sz)
+      ++g_evt_dbtuple_logical_deletes;
+
+    const bool needs_old_value =
+      writer(TUPLE_WRITER_NEEDS_OLD_VALUE, nullptr, nullptr, 0);
+    dbtuple * const rep =
+      alloc_spill(t, get_value_start(), old_sz, new_sz,
+                  this, true, needs_old_value);
+    if (v)
+      writer(TUPLE_WRITER_DO_WRITE, v, rep->get_value_start(), size);
+    INVARIANT(rep->is_latest());
+    INVARIANT(rep->size == new_sz);
+    INVARIANT(new_sz || rep->is_deleting()); // set by alloc_spill()
+    clear_latest();
+    ++g_evt_dbtuple_inplace_buf_insufficient_on_spill;
+    return write_record_ret(rep, this, true);
+#endif
+  }
+
+  // NB: we round up allocation sizes because jemalloc will do this
+  // internally anyways, so we might as well grab more usable space (really
+  // just internal vs external fragmentation)
+
+  static inline dbtuple *
+  alloc_first(size_type sz, bool acquire_lock)
+  {
+    INVARIANT(sz <= std::numeric_limits<node_size_type>::max());
+    const size_t max_alloc_sz =
+      std::numeric_limits<node_size_type>::max() + sizeof(dbtuple);
+    const size_t alloc_sz =
+      std::min(
+          util::round_up<size_t, allocator::LgAllocAlignment>(sizeof(dbtuple) + sz),
+          max_alloc_sz);
+    char *p = reinterpret_cast<char *>(rcu::s_instance.alloc(alloc_sz));
+    INVARIANT(p);
+    INVARIANT((alloc_sz - sizeof(dbtuple)) >= sz);
+    return new (p) dbtuple(
+        sz, alloc_sz - sizeof(dbtuple), acquire_lock);
+  }
+
+  static inline dbtuple *
+  alloc(tid_t version, struct dbtuple *base, bool set_latest)
+  {
+    const size_t max_alloc_sz =
+      std::numeric_limits<node_size_type>::max() + sizeof(dbtuple);
+    const size_t alloc_sz =
+      std::min(
+          util::round_up<size_t, allocator::LgAllocAlignment>(sizeof(dbtuple) + base->size),
+          max_alloc_sz);
+    char *p = reinterpret_cast<char *>(rcu::s_instance.alloc(alloc_sz));
+    INVARIANT(p);
+    return new (p) dbtuple(
+        version, base, alloc_sz - sizeof(dbtuple), set_latest);
+  }
+
+  static inline dbtuple *
+  alloc_spill(tid_t version, const_record_type value, size_type oldsz,
+              size_type newsz, struct dbtuple *next, bool set_latest,
+              bool copy_old_value)
+  {
+    INVARIANT(oldsz <= std::numeric_limits<node_size_type>::max());
+    INVARIANT(newsz <= std::numeric_limits<node_size_type>::max());
+
+    const size_t needed_sz =
+      copy_old_value ? std::max(newsz, oldsz) : newsz;
+    const size_t max_alloc_sz =
+      std::numeric_limits<node_size_type>::max() + sizeof(dbtuple);
+    const size_t alloc_sz =
+      std::min(
+          util::round_up<size_t, allocator::LgAllocAlignment>(sizeof(dbtuple) + needed_sz),
+          max_alloc_sz);
+    char *p = reinterpret_cast<char *>(rcu::s_instance.alloc(alloc_sz));
+    INVARIANT(p);
+    return new (p) dbtuple(
+        version, value, oldsz, newsz,
+        alloc_sz - sizeof(dbtuple), next, set_latest, copy_old_value);
+  }
+
+
+private:
+  static inline void
+  destruct_and_free(dbtuple *n)
+  {
+    const size_t alloc_sz = n->alloc_size + sizeof(*n);
+    n->~dbtuple();
+    rcu::s_instance.dealloc(n, alloc_sz);
+  }
+
+public:
+  static void
+  deleter(void *p)
+  {
+    dbtuple * const n = (dbtuple *) p;
+    INVARIANT(!n->is_latest());
+    INVARIANT(!n->is_locked());
+    INVARIANT(!n->is_modifying());
+    destruct_and_free(n);
+  }
+
+  static inline void
+  release(dbtuple *n)
+  {
+    if (unlikely(!n))
+      return;
+    INVARIANT(n->is_locked());
+    INVARIANT(!n->is_latest());
+    rcu::s_instance.free_with_fn(n, deleter);
+  }
+
+  static inline void
+  release_no_rcu(dbtuple *n)
+  {
+    if (unlikely(!n))
+      return;
+    INVARIANT(!n->is_latest());
+    destruct_and_free(n);
+  }
+
+  static std::string
+  VersionInfoStr(version_t v);
+
+}
+#if !defined(TUPLE_CHECK_KEY) && \
+    !defined(CHECK_INVARIANTS) && \
+    !defined(TUPLE_LOCK_OWNERSHIP_CHECKING)
+PACKED
+#endif
+;
+
+#endif /* _NDB_TUPLE_H_ */
diff --git a/silo/txn.cc b/silo/txn.cc
new file mode 100644 (file)
index 0000000..f945202
--- /dev/null
@@ -0,0 +1,67 @@
+#include "macros.h"
+#include "amd64.h"
+#include "txn.h"
+#include "txn_proto2_impl.h"
+#include "txn_btree.h"
+#include "lockguard.h"
+#include "scopedperf.hh"
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <utility>
+
+using namespace std;
+using namespace util;
+
+static string
+proto1_version_str(uint64_t v) UNUSED;
+static string
+proto1_version_str(uint64_t v)
+{
+  ostringstream b;
+  b << v;
+  return b.str();
+}
+
+static string
+proto2_version_str(uint64_t v) UNUSED;
+static string
+proto2_version_str(uint64_t v)
+{
+  ostringstream b;
+  b << "[core=" << transaction_proto2_static::CoreId(v) << " | n="
+    << transaction_proto2_static::NumId(v) << " | epoch="
+    << transaction_proto2_static::EpochId(v) << "]";
+  return b.str();
+}
+
+// XXX(stephentu): hacky!
+string (*g_proto_version_str)(uint64_t v) = proto2_version_str;
+
+CLASS_STATIC_COUNTER_IMPL(transaction_base, scopedperf::tsc_ctr, g_txn_commit_probe0, g_txn_commit_probe0_cg);
+CLASS_STATIC_COUNTER_IMPL(transaction_base, scopedperf::tsc_ctr, g_txn_commit_probe1, g_txn_commit_probe1_cg);
+CLASS_STATIC_COUNTER_IMPL(transaction_base, scopedperf::tsc_ctr, g_txn_commit_probe2, g_txn_commit_probe2_cg);
+CLASS_STATIC_COUNTER_IMPL(transaction_base, scopedperf::tsc_ctr, g_txn_commit_probe3, g_txn_commit_probe3_cg);
+CLASS_STATIC_COUNTER_IMPL(transaction_base, scopedperf::tsc_ctr, g_txn_commit_probe4, g_txn_commit_probe4_cg);
+CLASS_STATIC_COUNTER_IMPL(transaction_base, scopedperf::tsc_ctr, g_txn_commit_probe5, g_txn_commit_probe5_cg);
+CLASS_STATIC_COUNTER_IMPL(transaction_base, scopedperf::tsc_ctr, g_txn_commit_probe6, g_txn_commit_probe6_cg);
+
+#define EVENT_COUNTER_IMPL_X(x) \
+  event_counter transaction_base::g_ ## x ## _ctr(#x);
+ABORT_REASONS(EVENT_COUNTER_IMPL_X)
+#undef EVENT_COUNTER_IMPL_X
+
+event_counter transaction_base::g_evt_read_logical_deleted_node_search
+    ("read_logical_deleted_node_search");
+event_counter transaction_base::g_evt_read_logical_deleted_node_scan
+    ("read_logical_deleted_node_scan");
+event_counter transaction_base::g_evt_dbtuple_write_search_failed
+    ("dbtuple_write_search_failed");
+event_counter transaction_base::g_evt_dbtuple_write_insert_failed
+    ("dbtuple_write_insert_failed");
+
+event_counter transaction_base::evt_local_search_lookups("local_search_lookups");
+event_counter transaction_base::evt_local_search_write_set_hits("local_search_write_set_hits");
+event_counter transaction_base::evt_dbtuple_latest_replacement("dbtuple_latest_replacement");
diff --git a/silo/txn.h b/silo/txn.h
new file mode 100644 (file)
index 0000000..46a9bd0
--- /dev/null
@@ -0,0 +1,865 @@
+#ifndef _NDB_TXN_H_
+#define _NDB_TXN_H_
+
+#include <malloc.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#include <map>
+#include <iostream>
+#include <vector>
+#include <string>
+#include <utility>
+#include <stdexcept>
+#include <limits>
+#include <type_traits>
+#include <tuple>
+
+#include <unordered_map>
+
+#include "amd64.h"
+#include "btree_choice.h"
+#include "core.h"
+#include "counter.h"
+#include "macros.h"
+#include "varkey.h"
+#include "util.h"
+#include "rcu.h"
+#include "thread.h"
+#include "spinlock.h"
+#include "small_unordered_map.h"
+#include "static_unordered_map.h"
+#include "static_vector.h"
+#include "prefetch.h"
+#include "tuple.h"
+#include "scopedperf.hh"
+#include "marked_ptr.h"
+#include "ndb_type_traits.h"
+
+// forward decl
+template <template <typename> class Transaction, typename P>
+  class base_txn_btree;
+
+class transaction_unusable_exception {};
+class transaction_read_only_exception {};
+
+// XXX: hacky
+extern std::string (*g_proto_version_str)(uint64_t v);
+
+// base class with very simple definitions- nothing too exciting yet
+class transaction_base {
+  template <template <typename> class T, typename P>
+    friend class base_txn_btree;
+public:
+
+  typedef dbtuple::tid_t tid_t;
+  typedef dbtuple::size_type size_type;
+  typedef dbtuple::string_type string_type;
+
+  // TXN_EMBRYO - the transaction object has been allocated but has not
+  // done any operations yet
+  enum txn_state { TXN_EMBRYO, TXN_ACTIVE, TXN_COMMITED, TXN_ABRT, };
+
+  enum {
+    // use the low-level scan protocol for checking scan consistency,
+    // instead of keeping track of absent ranges
+    TXN_FLAG_LOW_LEVEL_SCAN = 0x1,
+
+    // true to mark a read-only transaction- if a txn marked read-only
+    // does a write, a transaction_read_only_exception is thrown and the
+    // txn is aborted
+    TXN_FLAG_READ_ONLY = 0x2,
+
+    // XXX: more flags in the future, things like consistency levels
+  };
+
+#define ABORT_REASONS(x) \
+    x(ABORT_REASON_NONE) \
+    x(ABORT_REASON_USER) \
+    x(ABORT_REASON_UNSTABLE_READ) \
+    x(ABORT_REASON_FUTURE_TID_READ) \
+    x(ABORT_REASON_NODE_SCAN_WRITE_VERSION_CHANGED) \
+    x(ABORT_REASON_NODE_SCAN_READ_VERSION_CHANGED) \
+    x(ABORT_REASON_WRITE_NODE_INTERFERENCE) \
+    x(ABORT_REASON_INSERT_NODE_INTERFERENCE) \
+    x(ABORT_REASON_READ_NODE_INTEREFERENCE) \
+    x(ABORT_REASON_READ_ABSENCE_INTEREFERENCE)
+
+  enum abort_reason {
+#define ENUM_X(x) x,
+    ABORT_REASONS(ENUM_X)
+#undef ENUM_X
+  };
+
+  static const char *
+  AbortReasonStr(abort_reason reason)
+  {
+    switch (reason) {
+#define CASE_X(x) case x: return #x;
+    ABORT_REASONS(CASE_X)
+#undef CASE_X
+    default:
+      break;
+    }
+    ALWAYS_ASSERT(false);
+    return 0;
+  }
+
+  transaction_base(uint64_t flags)
+    : state(TXN_EMBRYO),
+      reason(ABORT_REASON_NONE),
+      flags(flags) {}
+
+  transaction_base(const transaction_base &) = delete;
+  transaction_base(transaction_base &&) = delete;
+  transaction_base &operator=(const transaction_base &) = delete;
+
+protected:
+#define EVENT_COUNTER_DEF_X(x) \
+  static event_counter g_ ## x ## _ctr;
+  ABORT_REASONS(EVENT_COUNTER_DEF_X)
+#undef EVENT_COUNTER_DEF_X
+
+  static event_counter *
+  AbortReasonCounter(abort_reason reason)
+  {
+    switch (reason) {
+#define EVENT_COUNTER_CASE_X(x) case x: return &g_ ## x ## _ctr;
+    ABORT_REASONS(EVENT_COUNTER_CASE_X)
+#undef EVENT_COUNTER_CASE_X
+    default:
+      break;
+    }
+    ALWAYS_ASSERT(false);
+    return 0;
+  }
+
+public:
+
+  // only fires during invariant checking
+  inline void
+  ensure_active()
+  {
+    if (state == TXN_EMBRYO)
+      state = TXN_ACTIVE;
+    INVARIANT(state == TXN_ACTIVE);
+  }
+
+  inline uint64_t
+  get_flags() const
+  {
+    return flags;
+  }
+
+protected:
+
+  // the read set is a mapping from (tuple -> tid_read).
+  // "write_set" is used to indicate if this read tuple
+  // also belongs in the write set.
+  struct read_record_t {
+    constexpr read_record_t() : tuple(), t() {}
+    constexpr read_record_t(const dbtuple *tuple, tid_t t)
+      : tuple(tuple), t(t) {}
+    inline const dbtuple *
+    get_tuple() const
+    {
+      return tuple;
+    }
+    inline tid_t
+    get_tid() const
+    {
+      return t;
+    }
+  private:
+    const dbtuple *tuple;
+    tid_t t;
+  };
+
+  friend std::ostream &
+  operator<<(std::ostream &o, const read_record_t &r);
+
+  // the write set is logically a mapping from (tuple -> value_to_write).
+  struct write_record_t {
+    enum {
+      FLAGS_INSERT  = 0x1,
+      FLAGS_DOWRITE = 0x1 << 1,
+    };
+
+    constexpr inline write_record_t()
+      : tuple(), k(), r(), w(), btr()
+    {}
+
+    // all inputs are assumed to be stable
+    inline write_record_t(dbtuple *tuple,
+                          const string_type *k,
+                          const void *r,
+                          dbtuple::tuple_writer_t w,
+                          concurrent_btree *btr,
+                          bool insert)
+      : tuple(tuple),
+        k(k),
+        r(r),
+        w(w),
+        btr(btr)
+    {
+      this->btr.set_flags(insert ? FLAGS_INSERT : 0);
+    }
+    inline dbtuple *
+    get_tuple()
+    {
+      return tuple;
+    }
+    inline const dbtuple *
+    get_tuple() const
+    {
+      return tuple;
+    }
+    inline bool
+    is_insert() const
+    {
+      return btr.get_flags() & FLAGS_INSERT;
+    }
+    inline bool
+    do_write() const
+    {
+      return btr.get_flags() & FLAGS_DOWRITE;
+    }
+    inline void
+    set_do_write()
+    {
+      INVARIANT(!do_write());
+      btr.or_flags(FLAGS_DOWRITE);
+    }
+    inline concurrent_btree *
+    get_btree() const
+    {
+      return btr.get();
+    }
+    inline const string_type &
+    get_key() const
+    {
+      return *k;
+    }
+    inline const void *
+    get_value() const
+    {
+      return r;
+    }
+    inline dbtuple::tuple_writer_t
+    get_writer() const
+    {
+      return w;
+    }
+  private:
+    dbtuple *tuple;
+    const string_type *k;
+    const void *r;
+    dbtuple::tuple_writer_t w;
+    marked_ptr<concurrent_btree> btr; // first bit for inserted, 2nd for dowrite
+  };
+
+  friend std::ostream &
+  operator<<(std::ostream &o, const write_record_t &r);
+
+  // the absent set is a mapping from (btree_node -> version_number).
+  struct absent_record_t { uint64_t version; };
+
+  friend std::ostream &
+  operator<<(std::ostream &o, const absent_record_t &r);
+
+  struct dbtuple_write_info {
+    enum {
+      FLAGS_LOCKED = 0x1,
+      FLAGS_INSERT = 0x1 << 1,
+    };
+    dbtuple_write_info() : tuple(), entry(nullptr), pos() {}
+    dbtuple_write_info(dbtuple *tuple, write_record_t *entry,
+                       bool is_insert, size_t pos)
+      : tuple(tuple), entry(entry), pos(pos)
+    {
+      if (is_insert)
+        this->tuple.set_flags(FLAGS_LOCKED | FLAGS_INSERT);
+    }
+    // XXX: for searching only
+    explicit dbtuple_write_info(const dbtuple *tuple)
+      : tuple(const_cast<dbtuple *>(tuple)), entry(), pos() {}
+    inline dbtuple *
+    get_tuple()
+    {
+      return tuple.get();
+    }
+    inline const dbtuple *
+    get_tuple() const
+    {
+      return tuple.get();
+    }
+    inline ALWAYS_INLINE void
+    mark_locked()
+    {
+      INVARIANT(!is_locked());
+      tuple.or_flags(FLAGS_LOCKED);
+      INVARIANT(is_locked());
+    }
+    inline ALWAYS_INLINE bool
+    is_locked() const
+    {
+      return tuple.get_flags() & FLAGS_LOCKED;
+    }
+    inline ALWAYS_INLINE bool
+    is_insert() const
+    {
+      return tuple.get_flags() & FLAGS_INSERT;
+    }
+    inline ALWAYS_INLINE
+    bool operator<(const dbtuple_write_info &o) const
+    {
+      // the unique key is [tuple, !is_insert, pos]
+      return tuple < o.tuple ||
+             (tuple == o.tuple && !is_insert() < !o.is_insert()) ||
+             (tuple == o.tuple && !is_insert() == !o.is_insert() && pos < o.pos);
+    }
+    marked_ptr<dbtuple> tuple;
+    write_record_t *entry;
+    size_t pos;
+  };
+
+
+  static event_counter g_evt_read_logical_deleted_node_search;
+  static event_counter g_evt_read_logical_deleted_node_scan;
+  static event_counter g_evt_dbtuple_write_search_failed;
+  static event_counter g_evt_dbtuple_write_insert_failed;
+
+  static event_counter evt_local_search_lookups;
+  static event_counter evt_local_search_write_set_hits;
+  static event_counter evt_dbtuple_latest_replacement;
+
+  CLASS_STATIC_COUNTER_DECL(scopedperf::tsc_ctr, g_txn_commit_probe0, g_txn_commit_probe0_cg);
+  CLASS_STATIC_COUNTER_DECL(scopedperf::tsc_ctr, g_txn_commit_probe1, g_txn_commit_probe1_cg);
+  CLASS_STATIC_COUNTER_DECL(scopedperf::tsc_ctr, g_txn_commit_probe2, g_txn_commit_probe2_cg);
+  CLASS_STATIC_COUNTER_DECL(scopedperf::tsc_ctr, g_txn_commit_probe3, g_txn_commit_probe3_cg);
+  CLASS_STATIC_COUNTER_DECL(scopedperf::tsc_ctr, g_txn_commit_probe4, g_txn_commit_probe4_cg);
+  CLASS_STATIC_COUNTER_DECL(scopedperf::tsc_ctr, g_txn_commit_probe5, g_txn_commit_probe5_cg);
+  CLASS_STATIC_COUNTER_DECL(scopedperf::tsc_ctr, g_txn_commit_probe6, g_txn_commit_probe6_cg);
+
+  txn_state state;
+  abort_reason reason;
+  const uint64_t flags;
+};
+
+// type specializations
+namespace private_ {
+  template <>
+  struct is_trivially_destructible<transaction_base::read_record_t> {
+    static const bool value = true;
+  };
+
+  template <>
+  struct is_trivially_destructible<transaction_base::write_record_t> {
+    static const bool value = true;
+  };
+
+  template <>
+  struct is_trivially_destructible<transaction_base::absent_record_t> {
+    static const bool value = true;
+  };
+
+  template <>
+  struct is_trivially_destructible<transaction_base::dbtuple_write_info> {
+    static const bool value = true;
+  };
+}
+
+inline ALWAYS_INLINE std::ostream &
+operator<<(std::ostream &o, const transaction_base::read_record_t &r)
+{
+  //o << "[tuple=" << util::hexify(r.get_tuple())
+  o << "[tuple=" << *r.get_tuple()
+    << ", tid_read=" << g_proto_version_str(r.get_tid())
+    << "]";
+  return o;
+}
+
+inline ALWAYS_INLINE std::ostream &
+operator<<(
+    std::ostream &o,
+    const transaction_base::write_record_t &r)
+{
+  o << "[tuple=" << r.get_tuple()
+    << ", key=" << util::hexify(r.get_key())
+    << ", value=" << util::hexify(r.get_value())
+    << ", insert=" << r.is_insert()
+    << ", do_write=" << r.do_write()
+    << ", btree=" << r.get_btree()
+    << "]";
+  return o;
+}
+
+inline ALWAYS_INLINE std::ostream &
+operator<<(std::ostream &o, const transaction_base::absent_record_t &r)
+{
+  o << "[v=" << r.version << "]";
+  return o;
+}
+
+struct default_transaction_traits {
+  static const size_t read_set_expected_size = SMALL_SIZE_MAP;
+  static const size_t absent_set_expected_size = EXTRA_SMALL_SIZE_MAP;
+  static const size_t write_set_expected_size = SMALL_SIZE_MAP;
+  static const bool stable_input_memory = false;
+  static const bool hard_expected_sizes = false; // true if the expected sizes are hard maximums
+  static const bool read_own_writes = true; // if we read a key which we previous put(), are we guaranteed
+                                            // to read our latest (uncommited) values? this comes at a
+                                            // performance penality [you should not need this behavior to
+                                            // write txns, since you *know* the values you inserted]
+
+  typedef util::default_string_allocator StringAllocator;
+};
+
+struct default_stable_transaction_traits : public default_transaction_traits {
+  static const bool stable_input_memory = true;
+};
+
+template <template <typename> class Protocol, typename Traits>
+class transaction : public transaction_base {
+  // XXX: weaker than necessary
+  template <template <typename> class, typename>
+    friend class base_txn_btree;
+  friend Protocol<Traits>;
+
+public:
+
+  // KeyWriter is expected to implement:
+  // [1-arg constructor]
+  //   KeyWriter(const Key *)
+  // [fully materialize]
+  //   template <typename StringAllocator>
+  //   const std::string * fully_materialize(bool, StringAllocator &)
+
+  // ValueWriter is expected to implement:
+  // [1-arg constructor]
+  //   ValueWriter(const Value *, ValueInfo)
+  // [compute new size from old value]
+  //   size_t compute_needed(const uint8_t *, size_t)
+  // [fully materialize]
+  //   template <typename StringAllocator>
+  //   const std::string * fully_materialize(bool, StringAllocator &)
+  // [perform write]
+  //   void operator()(uint8_t *, size_t)
+  //
+  // ValueWriter does not have to be move/copy constructable. The value passed
+  // into the ValueWriter constructor is guaranteed to be valid throughout the
+  // lifetime of a ValueWriter instance.
+
+  // KeyReader Interface
+  //
+  // KeyReader is a simple transformation from (const std::string &) => const Key &.
+  // The input is guaranteed to be stable, so it has a simple interface:
+  //
+  //   const Key &operator()(const std::string &)
+  //
+  // The KeyReader is expect to preserve the following property: After a call
+  // to operator(), but before the next, the returned value is guaranteed to be
+  // valid and remain stable.
+
+  // ValueReader Interface
+  //
+  // ValueReader is a more complex transformation from (const uint8_t *, size_t) => Value &.
+  // The input is not guaranteed to be stable, so it has a more complex interface:
+  //
+  //   template <typename StringAllocator>
+  //   bool operator()(const uint8_t *, size_t, StringAllocator &)
+  //
+  // This interface returns false if there was not enough buffer space to
+  // finish the read, true otherwise.  Note that this interface returning true
+  // does NOT mean that a read was stable, but it just means there were enough
+  // bytes in the buffer to perform the tentative read.
+  //
+  // Note that ValueReader also exposes a dup interface
+  //
+  //   template <typename StringAllocator>
+  //   void dup(const Value &, StringAllocator &)
+  //
+  // ValueReader also exposes a means to fetch results:
+  //
+  //   Value &results()
+  //
+  // The ValueReader is expected to preserve the following property: After a
+  // call to operator(), if it returns true, then the value returned from
+  // results() should remain valid and stable until the next call to
+  // operator().
+
+  //typedef typename P::Key key_type;
+  //typedef typename P::Value value_type;
+  //typedef typename P::ValueInfo value_info_type;
+
+  //typedef typename P::KeyWriter key_writer_type;
+  //typedef typename P::ValueWriter value_writer_type;
+
+  //typedef typename P::KeyReader key_reader_type;
+  //typedef typename P::SingleValueReader single_value_reader_type;
+  //typedef typename P::ValueReader value_reader_type;
+
+  typedef Traits traits_type;
+  typedef typename Traits::StringAllocator string_allocator_type;
+
+protected:
+  // data structures
+
+  inline ALWAYS_INLINE Protocol<Traits> *
+  cast()
+  {
+    return static_cast<Protocol<Traits> *>(this);
+  }
+
+  inline ALWAYS_INLINE const Protocol<Traits> *
+  cast() const
+  {
+    return static_cast<const Protocol<Traits> *>(this);
+  }
+
+  // XXX: we have baked in b-tree into the protocol- other indexes are possible
+  // but we would need to abstract it away. we don't bother for now.
+
+#ifdef USE_SMALL_CONTAINER_OPT
+  // XXX: use parameterized typedef to avoid duplication
+
+  // small types
+  typedef small_vector<
+    read_record_t,
+    traits_type::read_set_expected_size> read_set_map_small;
+  typedef small_vector<
+    write_record_t,
+    traits_type::write_set_expected_size> write_set_map_small;
+  typedef small_unordered_map<
+    const typename concurrent_btree::node_opaque_t *, absent_record_t,
+    traits_type::absent_set_expected_size> absent_set_map_small;
+
+  // static types
+  typedef static_vector<
+    read_record_t,
+    traits_type::read_set_expected_size> read_set_map_static;
+  typedef static_vector<
+    write_record_t,
+    traits_type::write_set_expected_size> write_set_map_static;
+  typedef static_unordered_map<
+    const typename concurrent_btree::node_opaque_t *, absent_record_t,
+    traits_type::absent_set_expected_size> absent_set_map_static;
+
+  // helper types for log writing
+  typedef small_vector<
+    uint32_t,
+    traits_type::write_set_expected_size> write_set_u32_vec_small;
+  typedef static_vector<
+    uint32_t,
+    traits_type::write_set_expected_size> write_set_u32_vec_static;
+
+  // use static types if the expected sizes are guarantees
+  typedef
+    typename std::conditional<
+      traits_type::hard_expected_sizes,
+      read_set_map_static, read_set_map_small>::type read_set_map;
+  typedef
+    typename std::conditional<
+      traits_type::hard_expected_sizes,
+      write_set_map_static, write_set_map_small>::type write_set_map;
+  typedef
+    typename std::conditional<
+      traits_type::hard_expected_sizes,
+      absent_set_map_static, absent_set_map_small>::type absent_set_map;
+  typedef
+    typename std::conditional<
+      traits_type::hard_expected_sizes,
+      write_set_u32_vec_static, write_set_u32_vec_small>::type write_set_u32_vec;
+
+#else
+  typedef std::vector<read_record_t> read_set_map;
+  typedef std::vector<write_record_t> write_set_map;
+  typedef std::vector<absent_record_t> absent_set_map;
+  typedef std::vector<uint32_t> write_set_u32_vec;
+#endif
+
+  template <typename T>
+    using write_set_sized_vec =
+      typename std::conditional<
+        traits_type::hard_expected_sizes,
+        static_vector<T, traits_type::write_set_expected_size>,
+        typename util::vec<T, traits_type::write_set_expected_size>::type
+      >::type;
+
+  // small type
+  typedef
+    typename util::vec<
+      dbtuple_write_info, traits_type::write_set_expected_size>::type
+    dbtuple_write_info_vec_small;
+
+  // static type
+  typedef
+    static_vector<
+      dbtuple_write_info, traits_type::write_set_expected_size>
+    dbtuple_write_info_vec_static;
+
+  // chosen type
+  typedef
+    typename std::conditional<
+      traits_type::hard_expected_sizes,
+      dbtuple_write_info_vec_static, dbtuple_write_info_vec_small>::type
+    dbtuple_write_info_vec;
+
+  static inline bool
+  sorted_dbtuples_contains(
+      const dbtuple_write_info_vec &dbtuples,
+      const dbtuple *tuple)
+  {
+    // XXX: skip binary search for small-sized dbtuples?
+    return std::binary_search(
+        dbtuples.begin(), dbtuples.end(),
+        dbtuple_write_info(tuple),
+        [](const dbtuple_write_info &lhs, const dbtuple_write_info &rhs)
+          { return lhs.get_tuple() < rhs.get_tuple(); });
+  }
+
+public:
+
+  inline transaction(uint64_t flags, string_allocator_type &sa);
+  inline ~transaction();
+
+  // returns TRUE on successful commit, FALSE on abort
+  // if doThrow, signals success by returning true, and
+  // failure by throwing an abort exception
+  bool commit(bool doThrow = false);
+
+  // abort() always succeeds
+  inline void
+  abort()
+  {
+    abort_impl(ABORT_REASON_USER);
+  }
+
+  void dump_debug_info() const;
+
+#ifdef DIE_ON_ABORT
+  void
+  abort_trap(abort_reason reason)
+  {
+    AbortReasonCounter(reason)->inc();
+    this->reason = reason; // for dump_debug_info() to see
+    dump_debug_info();
+    ::abort();
+  }
+#else
+  inline ALWAYS_INLINE void
+  abort_trap(abort_reason reason)
+  {
+    AbortReasonCounter(reason)->inc();
+  }
+#endif
+
+  std::map<std::string, uint64_t> get_txn_counters() const;
+
+  inline ALWAYS_INLINE bool
+  is_snapshot() const
+  {
+    return get_flags() & TXN_FLAG_READ_ONLY;
+  }
+
+  // for debugging purposes only
+  inline const read_set_map &
+  get_read_set() const
+  {
+    return read_set;
+  }
+
+  inline const write_set_map &
+  get_write_set() const
+  {
+    return write_set;
+  }
+
+  inline const absent_set_map &
+  get_absent_set() const
+  {
+    return absent_set;
+  }
+
+protected:
+  inline void abort_impl(abort_reason r);
+
+  // assumes lock on marker is held on marker by caller, and marker is the
+  // latest: removes marker from tree, and clears latest
+  void cleanup_inserted_tuple_marker(
+      dbtuple *marker, const std::string &key,
+      concurrent_btree *btr);
+
+  // low-level API for txn_btree
+
+  // try to insert a new "tentative" tuple into the underlying
+  // btree associated with the given context.
+  //
+  // if return.first is not null, then this function will
+  //   1) mutate the transaction such that the absent_set is aware of any
+  //      mutating changes made to the underlying btree.
+  //   2) add the new tuple to the write_set
+  //
+  // if return.second is true, then this txn should abort, because a conflict
+  // was detected w/ the absent_set.
+  //
+  // if return.first is not null, the returned tuple is locked()!
+  //
+  // if the return.first is null, then this function has no side effects.
+  //
+  // NOTE: !ret.first => !ret.second
+  // NOTE: assumes key/value are stable
+  std::pair< dbtuple *, bool >
+  try_insert_new_tuple(
+      concurrent_btree &btr,
+      const std::string *key,
+      const void *value,
+      dbtuple::tuple_writer_t writer);
+
+  // reads the contents of tuple into v
+  // within this transaction context
+  template <typename ValueReader>
+  bool
+  do_tuple_read(const dbtuple *tuple, ValueReader &value_reader);
+
+  void
+  do_node_read(const typename concurrent_btree::node_opaque_t *n, uint64_t version);
+
+public:
+  // expected public overrides
+
+  /**
+   * Can we overwrite prev with cur?
+   */
+  bool can_overwrite_record_tid(tid_t prev, tid_t cur) const;
+
+  inline string_allocator_type &
+  string_allocator()
+  {
+    return *sa;
+  }
+
+protected:
+  // expected protected overrides
+
+  /**
+   * create a new, unique TID for a txn. at the point which gen_commit_tid(),
+   * it still has not been decided whether or not this txn will commit
+   * successfully
+   */
+  tid_t gen_commit_tid(const dbtuple_write_info_vec &write_tuples);
+
+  bool can_read_tid(tid_t t) const;
+
+  // For GC handlers- note that on_dbtuple_spill() is called
+  // with the lock on ln held, to simplify GC code
+  //
+  // Is also called within an RCU read region
+  void on_dbtuple_spill(dbtuple *tuple_ahead, dbtuple *tuple);
+
+  // Called when the latest value written to ln is an empty
+  // (delete) marker. The protocol can then decide how to schedule
+  // the logical node for actual deletion
+  void on_logical_delete(dbtuple *tuple, const std::string &key, concurrent_btree *btr);
+
+  // if gen_commit_tid() is called, then on_tid_finish() will be called
+  // with the commit tid. before on_tid_finish() is called, state is updated
+  // with the resolution (commited, aborted) of this txn
+  void on_tid_finish(tid_t commit_tid);
+
+  void on_post_rcu_region_completion();
+
+protected:
+  inline void clear();
+
+  // SLOW accessor methods- used for invariant checking
+
+  typename read_set_map::iterator
+  find_read_set(const dbtuple *tuple)
+  {
+    // linear scan- returns the *first* entry found
+    // (a tuple can exist in the read_set more than once)
+    typename read_set_map::iterator it     = read_set.begin();
+    typename read_set_map::iterator it_end = read_set.end();
+    for (; it != it_end; ++it)
+      if (it->get_tuple() == tuple)
+        break;
+    return it;
+  }
+
+  inline typename read_set_map::const_iterator
+  find_read_set(const dbtuple *tuple) const
+  {
+    return const_cast<transaction *>(this)->find_read_set(tuple);
+  }
+
+  typename write_set_map::iterator
+  find_write_set(dbtuple *tuple)
+  {
+    // linear scan- returns the *first* entry found
+    // (a tuple can exist in the write_set more than once)
+    typename write_set_map::iterator it     = write_set.begin();
+    typename write_set_map::iterator it_end = write_set.end();
+    for (; it != it_end; ++it)
+      if (it->get_tuple() == tuple)
+        break;
+    return it;
+  }
+
+  inline typename write_set_map::const_iterator
+  find_write_set(const dbtuple *tuple) const
+  {
+    return const_cast<transaction *>(this)->find_write_set(tuple);
+  }
+
+  inline bool
+  handle_last_tuple_in_group(
+      dbtuple_write_info &info, bool did_group_insert);
+
+  read_set_map read_set;
+  write_set_map write_set;
+  absent_set_map absent_set;
+
+  string_allocator_type *sa;
+
+  unmanaged<scoped_rcu_region> rcu_guard_;
+};
+
+class transaction_abort_exception : public std::exception {
+public:
+  transaction_abort_exception(transaction_base::abort_reason r)
+    : r(r) {}
+  inline transaction_base::abort_reason
+  get_reason() const
+  {
+    return r;
+  }
+  virtual const char *
+  what() const throw()
+  {
+    return transaction_base::AbortReasonStr(r);
+  }
+private:
+  transaction_base::abort_reason r;
+};
+
+// XXX(stephentu): stupid hacks
+// XXX(stephentu): txn_epoch_sync is a misnomer
+template <template <typename> class Transaction>
+struct txn_epoch_sync {
+  // block until the next epoch
+  static inline void sync() {}
+  // finish any async jobs
+  static inline void finish() {}
+  // run this code when a benchmark worker finishes
+  static inline void thread_end() {}
+  // how many txns have we persisted in total, from
+  // the last reset invocation?
+  static inline std::pair<uint64_t, double>
+    compute_ntxn_persisted() { return {0, 0.0}; }
+  // reset the persisted counters
+  static inline void reset_ntxn_persisted() {}
+};
+
+#endif /* _NDB_TXN_H_ */
diff --git a/silo/txn_btree.cc b/silo/txn_btree.cc
new file mode 100644 (file)
index 0000000..1ab87e1
--- /dev/null
@@ -0,0 +1,1906 @@
+#include <unistd.h>
+#include <limits>
+#include <memory>
+#include <atomic>
+#include <mutex>
+
+#include "txn.h"
+#include "txn_proto2_impl.h"
+#include "txn_btree.h"
+#include "typed_txn_btree.h"
+#include "thread.h"
+#include "util.h"
+#include "macros.h"
+#include "tuple.h"
+#include "record/encoder.h"
+#include "record/inline_str.h"
+
+#include "scopedperf.hh"
+
+#if defined(NDB_MASSTREE)
+#define HAVE_REVERSE_RANGE_SCANS
+#endif
+
+using namespace std;
+using namespace util;
+uint64_t initial_timestamp;
+
+struct test_callback_ctr {
+  test_callback_ctr(size_t *ctr) : ctr(ctr) {}
+  inline bool
+  operator()(const concurrent_btree::string_type &k, const string &v) const
+  {
+    (*ctr)++;
+    return true;
+  }
+  size_t *const ctr;
+};
+
+// all combinations of txn flags to test
+static uint64_t TxnFlags[] = { 0, transaction_base::TXN_FLAG_LOW_LEVEL_SCAN };
+
+template <typename P>
+static void
+always_assert_cond_in_txn(
+    const P &t, bool cond,
+    const char *condstr, const char *func,
+    const char *filename, int lineno)
+{
+  if (likely(cond))
+    return;
+  static mutex g_report_lock;
+  std::lock_guard<mutex> guard(g_report_lock);
+  cerr << func << " (" << filename << ":" << lineno << ") - Condition `"
+       << condstr << "' failed!" << endl;
+  t.dump_debug_info();
+  sleep(1); // XXX(stephentu): give time for debug dump to reach console
+            // why doesn't flushing solve this?
+  abort();
+}
+
+#define ALWAYS_ASSERT_COND_IN_TXN(t, cond) \
+  always_assert_cond_in_txn(t, cond, #cond, __PRETTY_FUNCTION__, __FILE__, __LINE__)
+
+template <typename P>
+static inline void
+AssertSuccessfulCommit(P &t)
+{
+  ALWAYS_ASSERT_COND_IN_TXN(t, t.commit(false));
+}
+
+template <typename P>
+static inline void
+AssertFailedCommit(P &t)
+{
+  ALWAYS_ASSERT_COND_IN_TXN(t, !t.commit(false));
+}
+
+template <typename T>
+inline void
+AssertByteEquality(const T &t, const uint8_t * v, size_t sz)
+{
+  ALWAYS_ASSERT(sizeof(T) == sz);
+  bool success = memcmp(&t, v, sz) == 0;
+  if (!success) {
+    cerr << "expecting: " << hexify(string((const char *) &t, sizeof(T))) << endl;
+    cerr << "got      : " << hexify(string((const char *) v, sz)) << endl;
+    ALWAYS_ASSERT(false);
+  }
+}
+
+template <typename T>
+inline void
+AssertByteEquality(const T &t, const string &v)
+{
+  AssertByteEquality(t, (const uint8_t *) v.data(), v.size());
+}
+
+struct rec {
+  rec() : v() {}
+  rec(uint64_t v) : v(v) {}
+  uint64_t v;
+};
+
+static inline ostream &
+operator<<(ostream &o, const rec &r)
+{
+  o << "[rec=" << r.v << "]";
+  return o;
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test1()
+{
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+
+    VERBOSE(cerr << "Testing with flags=0x" << hexify(txn_flags) << endl);
+
+    txn_btree<TxnType> btr(sizeof(rec));
+    typename Traits::StringAllocator arena;
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      string v;
+      ALWAYS_ASSERT_COND_IN_TXN(t, !btr.search(t, u64_varkey(0), v));
+      btr.insert_object(t, u64_varkey(0), rec(0));
+      ALWAYS_ASSERT_COND_IN_TXN(t, btr.search(t, u64_varkey(0), v));
+      ALWAYS_ASSERT_COND_IN_TXN(t, !v.empty());
+      AssertByteEquality(rec(0), v);
+      AssertSuccessfulCommit(t);
+      VERBOSE(cerr << "------" << endl);
+    }
+
+    {
+      TxnType<Traits>
+        t0(txn_flags, arena), t1(txn_flags, arena);
+      string v0, v1;
+
+      ALWAYS_ASSERT_COND_IN_TXN(t0, btr.search(t0, u64_varkey(0), v0));
+      ALWAYS_ASSERT_COND_IN_TXN(t0, !v0.empty());
+      AssertByteEquality(rec(0), v0);
+
+      btr.insert_object(t0, u64_varkey(0), rec(1));
+      ALWAYS_ASSERT_COND_IN_TXN(t1, btr.search(t1, u64_varkey(0), v1));
+      ALWAYS_ASSERT_COND_IN_TXN(t1, !v1.empty());
+
+      if (t1.is_snapshot())
+        // we don't read-uncommitted for consistent snapshot
+        AssertByteEquality(rec(0), v1);
+
+      AssertSuccessfulCommit(t0);
+      try {
+        t1.commit(true);
+        // if we have a consistent snapshot, then this txn should not abort
+        ALWAYS_ASSERT_COND_IN_TXN(t1, t1.is_snapshot());
+      } catch (transaction_abort_exception &e) {
+        // if we dont have a snapshot, then we expect an abort
+        ALWAYS_ASSERT_COND_IN_TXN(t1, !t1.is_snapshot());
+      }
+      VERBOSE(cerr << "------" << endl);
+    }
+
+    {
+      // racy insert
+      TxnType<Traits>
+        t0(txn_flags, arena), t1(txn_flags, arena);
+      string v0, v1;
+
+      ALWAYS_ASSERT_COND_IN_TXN(t0, btr.search(t0, u64_varkey(0), v0));
+      ALWAYS_ASSERT_COND_IN_TXN(t0, !v0.empty());
+      AssertByteEquality(rec(1), v0);
+
+      btr.insert_object(t0, u64_varkey(0), rec(2));
+      ALWAYS_ASSERT_COND_IN_TXN(t1, btr.search(t1, u64_varkey(0), v1));
+      ALWAYS_ASSERT_COND_IN_TXN(t1, !v1.empty());
+      // our API allows v1 to be a dirty read though (t1 will abort)
+
+      btr.insert_object(t1, u64_varkey(0), rec(3));
+
+      AssertSuccessfulCommit(t0);
+      AssertFailedCommit(t1);
+      VERBOSE(cerr << "------" << endl);
+    }
+
+    {
+      // racy scan
+      TxnType<Traits>
+        t0(txn_flags, arena), t1(txn_flags, arena);
+
+      const u64_varkey vend(5);
+      size_t ctr = 0;
+      test_callback_ctr cb(&ctr);
+      btr.search_range(t0, u64_varkey(1), &vend, cb);
+      ALWAYS_ASSERT_COND_IN_TXN(t0, ctr == 0);
+
+      btr.insert_object(t1, u64_varkey(2), rec(4));
+      AssertSuccessfulCommit(t1);
+
+      btr.insert_object(t0, u64_varkey(0), rec(0));
+      AssertFailedCommit(t0);
+      VERBOSE(cerr << "------" << endl);
+    }
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      const u64_varkey vend(20);
+      size_t ctr = 0;
+      test_callback_ctr cb(&ctr);
+      btr.search_range(t, u64_varkey(10), &vend, cb);
+      ALWAYS_ASSERT_COND_IN_TXN(t, ctr == 0);
+      btr.insert_object(t, u64_varkey(15), rec(5));
+      AssertSuccessfulCommit(t);
+      VERBOSE(cerr << "------" << endl);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+struct bufrec { char buf[256]; };
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test2()
+{
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+    txn_btree<TxnType> btr(sizeof(bufrec));
+    typename Traits::StringAllocator arena;
+    bufrec r;
+    NDB_MEMSET(r.buf, 'a', ARRAY_NELEMS(r.buf));
+    for (size_t i = 0; i < 100; i++) {
+      TxnType<Traits> t(txn_flags, arena);
+      btr.insert_object(t, u64_varkey(i), r);
+      AssertSuccessfulCommit(t);
+    }
+    for (size_t i = 0; i < 100; i++) {
+      TxnType<Traits> t(txn_flags, arena);
+      string v;
+      ALWAYS_ASSERT_COND_IN_TXN(t, btr.search(t, u64_varkey(i), v));
+      AssertByteEquality(r, v);
+      AssertSuccessfulCommit(t);
+    }
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test_absent_key_race()
+{
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+
+    {
+      TxnType<Traits>
+        t0(txn_flags, arena), t1(txn_flags, arena);
+      string v0, v1;
+      ALWAYS_ASSERT_COND_IN_TXN(t0, !btr.search(t0, u64_varkey(0), v0));
+      ALWAYS_ASSERT_COND_IN_TXN(t1, !btr.search(t1, u64_varkey(0), v1));
+
+      // t0 does a write
+      btr.insert_object(t0, u64_varkey(0), rec(1));
+
+      // t1 does a write
+      btr.insert_object(t1, u64_varkey(0), rec(1));
+
+      // t0 should win, t1 should abort
+      AssertSuccessfulCommit(t0);
+      AssertFailedCommit(t1);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test_inc_value_size()
+{
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+    const size_t upper = numeric_limits<dbtuple::node_size_type>::max();
+    for (size_t i = 1; i < upper; i++) {
+      const string v(i, 'a');
+      TxnType<Traits> t(txn_flags, arena);
+      btr.insert(t, u64_varkey(0), (const uint8_t *) v.data(), v.size());
+      AssertSuccessfulCommit(t);
+    }
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test_multi_btree()
+{
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+    txn_btree<TxnType> btr0, btr1;
+    typename Traits::StringAllocator arena;
+    for (size_t i = 0; i < 100; i++) {
+      TxnType<Traits> t(txn_flags, arena);
+      btr0.insert_object(t, u64_varkey(i), rec(123));
+      btr1.insert_object(t, u64_varkey(i), rec(123));
+      AssertSuccessfulCommit(t);
+    }
+
+    for (size_t i = 0; i < 100; i++) {
+      TxnType<Traits> t(txn_flags, arena);
+      string v0, v1;
+      bool ret0 = btr0.search(t, u64_varkey(i), v0);
+      bool ret1 = btr1.search(t, u64_varkey(i), v1);
+      AssertSuccessfulCommit(t);
+      ALWAYS_ASSERT_COND_IN_TXN(t, ret0);
+      ALWAYS_ASSERT_COND_IN_TXN(t, !v0.empty());
+      ALWAYS_ASSERT_COND_IN_TXN(t, ret1);
+      ALWAYS_ASSERT_COND_IN_TXN(t, !v1.empty());
+      AssertByteEquality(rec(123), v0);
+      AssertByteEquality(rec(123), v1);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test_read_only_snapshot()
+{
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      btr.insert_object(t, u64_varkey(0), rec(0));
+      AssertSuccessfulCommit(t);
+    }
+
+    // XXX(stephentu): HACK! we need to wait for the GC to
+    // compute a new consistent snapshot version that includes this
+    // latest update
+    txn_epoch_sync<TxnType>::sync();
+
+    {
+      TxnType<Traits>
+        t0(txn_flags, arena),
+        t1(txn_flags | transaction_base::TXN_FLAG_READ_ONLY, arena);
+      string v0, v1;
+      ALWAYS_ASSERT_COND_IN_TXN(t0, btr.search(t0, u64_varkey(0), v0));
+      ALWAYS_ASSERT_COND_IN_TXN(t0, !v0.empty());
+      AssertByteEquality(rec(0), v0);
+
+      btr.insert_object(t0, u64_varkey(0), rec(1));
+
+      ALWAYS_ASSERT_COND_IN_TXN(t1, btr.search(t1, u64_varkey(0), v1));
+      ALWAYS_ASSERT_COND_IN_TXN(t1, !v1.empty());
+      AssertByteEquality(rec(0), v1);
+
+      AssertSuccessfulCommit(t0);
+      AssertSuccessfulCommit(t1);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+namespace test_long_keys_ns {
+
+static inline string
+make_long_key(int32_t a, int32_t b, int32_t c, int32_t d) {
+  char buf[4 * sizeof(int32_t)];
+  int32_t *p = (int32_t *) &buf[0];
+  *p++ = a;
+  *p++ = b;
+  *p++ = c;
+  *p++ = d;
+  return string(&buf[0], ARRAY_NELEMS(buf));
+}
+
+template <template <typename> class Protocol>
+class counting_scan_callback : public txn_btree<Protocol>::search_range_callback {
+public:
+  counting_scan_callback(uint64_t expect) : ctr(0), expect(expect) {}
+
+  virtual bool
+  invoke(const typename txn_btree<Protocol>::keystring_type &k, const string &v)
+  {
+    AssertByteEquality(rec(expect), v);
+    ctr++;
+    return true;
+  }
+  size_t ctr;
+  uint64_t expect;
+};
+
+}
+
+namespace test_insert_same_key_ns {
+  struct rec0 { rec0(int ch) { NDB_MEMSET(&buf[0], ch, sizeof(buf)); } char buf[8];    };
+  struct rec1 { rec1(int ch) { NDB_MEMSET(&buf[0], ch, sizeof(buf)); } char buf[128];  };
+  struct rec2 { rec2(int ch) { NDB_MEMSET(&buf[0], ch, sizeof(buf)); } char buf[1024]; };
+  struct rec3 { rec3(int ch) { NDB_MEMSET(&buf[0], ch, sizeof(buf)); } char buf[2048]; };
+  struct rec4 { rec4(int ch) { NDB_MEMSET(&buf[0], ch, sizeof(buf)); } char buf[4096]; };
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test_long_keys()
+{
+  using namespace test_long_keys_ns;
+  const size_t N = 10;
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      for (size_t a = 0; a < N; a++)
+        for (size_t b = 0; b < N; b++)
+          for (size_t c = 0; c < N; c++)
+            for (size_t d = 0; d < N; d++) {
+              const string k = make_long_key(a, b, c, d);
+              btr.insert_object(t, varkey(k), rec(1));
+            }
+      AssertSuccessfulCommit(t);
+    }
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      const string lowkey_s = make_long_key(1, 2, 3, 0);
+      const string highkey_s = make_long_key(1, 2, 3, N);
+      const varkey highkey(highkey_s);
+      counting_scan_callback<TxnType> c(1);
+      btr.search_range_call(t, varkey(lowkey_s), &highkey, c);
+      AssertSuccessfulCommit(t);
+      if (c.ctr != N)
+        cerr << "c.ctr: " << c.ctr << ", N: " << N << endl;
+      ALWAYS_ASSERT_COND_IN_TXN(t, c.ctr == N);
+    }
+
+#ifdef HAVE_REVERSE_RANGE_SCANS
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      const string lowkey_s = make_long_key(4, 5, 3, 0);
+      const string highkey_s = make_long_key(4, 5, 3, N);
+      const varkey lowkey(lowkey_s);
+      counting_scan_callback<TxnType> c(1);
+      btr.rsearch_range_call(t, varkey(highkey_s), &lowkey, c);
+      AssertSuccessfulCommit(t);
+      if (c.ctr != (N-1))
+        cerr << "c.ctr: " << c.ctr << ", N: " << N << endl;
+      ALWAYS_ASSERT_COND_IN_TXN(t, c.ctr == (N-1));
+    }
+#endif
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+
+  cerr << "test_long_keys passed" << endl;
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test_long_keys2()
+{
+  using namespace test_long_keys_ns;
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+
+    const uint8_t lowkey_cstr[] = {
+      0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x45, 0x49, 0x4E, 0x47,
+      0x41, 0x54, 0x49, 0x4F, 0x4E, 0x45, 0x49, 0x4E, 0x47, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00,
+    };
+    const string lowkey_s((const char *) &lowkey_cstr[0], ARRAY_NELEMS(lowkey_cstr));
+    const uint8_t highkey_cstr[] = {
+      0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x45, 0x49, 0x4E, 0x47,
+      0x41, 0x54, 0x49, 0x4F, 0x4E, 0x45, 0x49, 0x4E, 0x47, 0x00, 0x00, 0x00,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF
+    };
+    const string highkey_s((const char *) &highkey_cstr[0], ARRAY_NELEMS(highkey_cstr));
+
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      btr.insert_object(t, varkey(lowkey_s), rec(12345));
+      AssertSuccessfulCommit(t);
+    }
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      string v;
+      ALWAYS_ASSERT_COND_IN_TXN(t, btr.search(t, varkey(lowkey_s), v));
+      AssertByteEquality(rec(12345), v);
+      AssertSuccessfulCommit(t);
+    }
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      counting_scan_callback<TxnType> c(12345);
+      const varkey highkey(highkey_s);
+      btr.search_range_call(t, varkey(lowkey_s), &highkey, c);
+      AssertSuccessfulCommit(t);
+      ALWAYS_ASSERT_COND_IN_TXN(t, c.ctr == 1);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test_insert_same_key()
+{
+  using namespace test_insert_same_key_ns;
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      btr.insert_object(t, u64_varkey(0), rec0('a'));
+      btr.insert_object(t, u64_varkey(0), rec1('b'));
+      btr.insert_object(t, u64_varkey(0), rec2('c'));
+      AssertSuccessfulCommit(t);
+    }
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      btr.insert_object(t, u64_varkey(0), rec3('d'));
+      btr.insert_object(t, u64_varkey(0), rec4('e'));
+      AssertSuccessfulCommit(t);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+#define TESTREC_KEY_FIELDS(x, y) \
+  x(int32_t,k0) \
+  y(int32_t,k1)
+#define TESTREC_VALUE_FIELDS(x, y) \
+  x(int32_t,v0) \
+  y(int16_t,v1) \
+  y(inline_str_fixed<10>,v2)
+DO_STRUCT(testrec, TESTREC_KEY_FIELDS, TESTREC_VALUE_FIELDS)
+
+namespace test_typed_btree_ns {
+
+static const pair<testrec::key, testrec::value> scan_values[] = {
+  {testrec::key(10, 1), testrec::value(123456 + 1, 10, "A")},
+  {testrec::key(10, 2), testrec::value(123456 + 2, 10, "B")},
+  {testrec::key(10, 3), testrec::value(123456 + 3, 10, "C")},
+  {testrec::key(10, 4), testrec::value(123456 + 4, 10, "D")},
+  {testrec::key(10, 5), testrec::value(123456 + 5, 10, "E")},
+};
+
+template <template <typename> class Protocol>
+class scan_callback : public typed_txn_btree<Protocol, schema<testrec>>::search_range_callback {
+public:
+  constexpr scan_callback() : n(0) {}
+
+  virtual bool
+  invoke(const testrec::key &key,
+         const testrec::value &value)
+  {
+    ALWAYS_ASSERT(n < ARRAY_NELEMS(scan_values));
+    ALWAYS_ASSERT(scan_values[n].first == key);
+    ALWAYS_ASSERT(scan_values[n].second.v2 == value.v2);
+    n++;
+    return true;
+  }
+
+private:
+  size_t n;
+};
+
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+test_typed_btree()
+{
+  using namespace test_typed_btree_ns;
+
+  typedef typed_txn_btree<TxnType, schema<testrec>> ttxn_btree_type;
+  ttxn_btree_type btr;
+  typename Traits::StringAllocator arena;
+  typedef TxnType<Traits> txn_type;
+
+  const testrec::key k0(1, 1);
+  const testrec::value v0(2, 3, "hello");
+
+  {
+    txn_type t(0, arena);
+    testrec::value v;
+    ALWAYS_ASSERT_COND_IN_TXN(t, !btr.search(t, k0, v));
+    btr.insert(t, k0, v0);
+    AssertSuccessfulCommit(t);
+  }
+
+  {
+    txn_type t(0, arena);
+    testrec::value v;
+    ALWAYS_ASSERT_COND_IN_TXN(t, btr.search(t, k0, v));
+    ALWAYS_ASSERT_COND_IN_TXN(t, v0 == v);
+    AssertSuccessfulCommit(t);
+  }
+
+  {
+    txn_type t(0, arena);
+    testrec::value v;
+    const bool ret = btr.search(t, k0, v, FIELDS(1, 2));
+    ALWAYS_ASSERT_COND_IN_TXN(t, ret);
+    ALWAYS_ASSERT_COND_IN_TXN(t, v.v1 == v0.v1);
+    ALWAYS_ASSERT_COND_IN_TXN(t, v.v2 == v0.v2);
+    AssertSuccessfulCommit(t);
+  }
+
+  {
+    txn_type t(0, arena);
+    testrec::value v;
+    const bool ret = btr.search(t, k0, v, FIELDS(0, 2));
+    ALWAYS_ASSERT_COND_IN_TXN(t, ret);
+    ALWAYS_ASSERT_COND_IN_TXN(t, v.v0 == v0.v0);
+    ALWAYS_ASSERT_COND_IN_TXN(t, v.v2 == v0.v2);
+    AssertSuccessfulCommit(t);
+  }
+
+  {
+    txn_type t(0, arena);
+    for (size_t i = 0; i < ARRAY_NELEMS(scan_values); i++)
+      btr.insert(t, scan_values[i].first, scan_values[i].second);
+    AssertSuccessfulCommit(t);
+  }
+
+  {
+    txn_type t(0, arena);
+    const testrec::key begin(10, 0);
+    scan_callback<TxnType> cb;
+    btr.search_range_call(t, begin, nullptr, cb, false, FIELDS(2));
+    AssertSuccessfulCommit(t);
+  }
+
+  txn_epoch_sync<TxnType>::sync();
+  txn_epoch_sync<TxnType>::finish();
+
+  cerr << "test_typed_btree() passed" << endl;
+}
+
+template <template <typename> class Protocol>
+class txn_btree_worker : public ndb_thread {
+public:
+  txn_btree_worker(txn_btree<Protocol> &btr, uint64_t txn_flags)
+    : btr(&btr), txn_flags(txn_flags) {}
+  inline uint64_t get_txn_flags() const { return txn_flags; }
+protected:
+  txn_btree<Protocol> *const btr;
+  const uint64_t txn_flags;
+};
+
+namespace mp_stress_test_allocator_ns {
+
+  static const size_t nworkers = 28;
+  static const size_t nkeys = nworkers * 4;
+
+  static atomic<bool> running(true);
+
+  template <template <typename> class TxnType, typename Traits>
+  class worker : public txn_btree_worker<TxnType> {
+  public:
+    worker(unsigned id, txn_btree<TxnType> &btr, uint64_t txn_flags)
+      : txn_btree_worker<TxnType>(btr, txn_flags),
+        id(id), commits(0), aborts(0) {}
+    ~worker() {}
+    virtual void run()
+    {
+      rcu::s_instance.pin_current_thread(id);
+      fast_random r(reinterpret_cast<unsigned long>(this));
+      string v;
+      while (running.load()) {
+        typename Traits::StringAllocator arena;
+        TxnType<Traits> t(this->txn_flags, arena);
+        try {
+          // RMW on a small space of keys
+          const u64_varkey k(r.next() % nkeys);
+          ALWAYS_ASSERT_COND_IN_TXN(t, this->btr->search(t, k, v));
+          ((rec *) v.data())->v++;
+          this->btr->put(t, k, v);
+          t.commit(true);
+          commits++;
+        } catch (transaction_abort_exception &e) {
+          aborts++;
+        }
+      }
+    }
+    unsigned get_id() const { return id; }
+    unsigned get_commits() const { return commits; }
+    unsigned get_aborts() const { return aborts; }
+  private:
+    unsigned id;
+    unsigned commits;
+    unsigned aborts;
+  };
+
+};
+
+template <template <typename> class TxnType, typename Traits>
+static void
+mp_stress_test_allocator()
+{
+  using namespace mp_stress_test_allocator_ns;
+  txn_btree<TxnType> btr;
+  {
+    rcu::s_instance.pin_current_thread(0);
+    typename Traits::StringAllocator arena;
+    TxnType<Traits> t(0, arena);
+    for (size_t i = 0; i < nkeys; i++)
+      btr.insert_object(t, u64_varkey(i), rec(0));
+    AssertSuccessfulCommit(t);
+  }
+  vector< shared_ptr< worker<TxnType, Traits> > > v;
+  for (size_t i = 0; i < nworkers; i++)
+    v.emplace_back(new worker<TxnType, Traits>(i, btr, 0));
+  for (auto &p : v)
+    p->start();
+  sleep(60);
+  running.store(false);
+  for (auto &p : v) {
+    p->join();
+    cerr << "worker " << p->get_id()
+         << " commits " << p->get_commits()
+         << " aborts " << p->get_aborts()
+         << endl;
+  }
+  cerr << "mp_stress_test_allocator passed" << endl;
+}
+
+namespace mp_stress_test_insert_removes_ns {
+  struct big_rec {
+    static const size_t S = numeric_limits<dbtuple::node_size_type>::max();
+    char buf[S];
+  };
+  static const size_t nworkers = 4;
+  static atomic<bool> running(true);
+  template <template <typename> class TxnType, typename Traits>
+  class worker : public txn_btree_worker<TxnType> {
+  public:
+    worker(txn_btree<TxnType> &btr, uint64_t txn_flags)
+      : txn_btree_worker<TxnType>(btr, txn_flags) {}
+    ~worker() {}
+    virtual void run()
+    {
+      fast_random r(reinterpret_cast<unsigned long>(this));
+      while (running.load()) {
+        typename Traits::StringAllocator arena;
+        TxnType<Traits> t(this->txn_flags, arena);
+        try {
+          switch (r.next() % 3) {
+          case 0:
+            this->btr->insert_object(t, u64_varkey(0), rec(1));
+            break;
+          case 1:
+            this->btr->insert_object(t, u64_varkey(0), big_rec());
+            break;
+          case 2:
+            this->btr->remove(t, u64_varkey(0));
+            break;
+          }
+          t.commit(true);
+        } catch (transaction_abort_exception &e) {
+        }
+      }
+    }
+  };
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+mp_stress_test_insert_removes()
+{
+  using namespace mp_stress_test_insert_removes_ns;
+  txn_btree<TxnType> btr;
+  vector< shared_ptr< worker<TxnType, Traits> > > v;
+  for (size_t i = 0; i < nworkers; i++)
+    v.emplace_back(new worker<TxnType, Traits>(btr, 0));
+  for (auto &p : v)
+    p->start();
+  sleep(5); // let many epochs pass
+  running.store(false);
+  for (auto &p : v)
+    p->join();
+
+}
+
+namespace mp_test1_ns {
+  // read-modify-write test (counters)
+
+  const size_t niters = 1000;
+
+  template <template <typename> class TxnType, typename Traits>
+  class worker : public txn_btree_worker<TxnType> {
+  public:
+    worker(txn_btree<TxnType> &btr, uint64_t txn_flags)
+      : txn_btree_worker<TxnType>(btr, txn_flags) {}
+    ~worker() {}
+    virtual void run()
+    {
+      for (size_t i = 0; i < niters; i++) {
+      retry:
+        typename Traits::StringAllocator arena;
+        TxnType<Traits> t(this->txn_flags, arena);
+        try {
+          rec r;
+          string v;
+          if (!this->btr->search(t, u64_varkey(0), v)) {
+            r.v = 1;
+          } else {
+            r = *((const rec *) v.data());
+            r.v++;
+          }
+          this->btr->insert_object(t, u64_varkey(0), r);
+          t.commit(true);
+        } catch (transaction_abort_exception &e) {
+          goto retry;
+        }
+      }
+    }
+  };
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+mp_test1()
+{
+  using namespace mp_test1_ns;
+
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+
+    worker<TxnType, Traits> w0(btr, txn_flags);
+    worker<TxnType, Traits> w1(btr, txn_flags);
+    worker<TxnType, Traits> w2(btr, txn_flags);
+    worker<TxnType, Traits> w3(btr, txn_flags);
+
+    w0.start(); w1.start(); w2.start(); w3.start();
+    w0.join(); w1.join(); w2.join(); w3.join();
+
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      string v;
+      ALWAYS_ASSERT_COND_IN_TXN(t, btr.search(t, u64_varkey(0), v));
+      ALWAYS_ASSERT_COND_IN_TXN(t, !v.empty());
+      const uint64_t rv = ((const rec *) v.data())->v;
+      if (rv != (niters * 4))
+        cerr << "v: " << rv << ", expected: " << (niters * 4) << endl;
+      ALWAYS_ASSERT_COND_IN_TXN(t, rv == (niters * 4));
+      AssertSuccessfulCommit(t);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+namespace mp_test2_ns {
+
+  static const uint64_t ctr_key = 0;
+  static const uint64_t range_begin = 100;
+  static const uint64_t range_end = 150;
+
+  struct control_rec {
+    uint32_t count_;
+    uint32_t values_[range_end - range_begin];
+    control_rec()
+      : count_(0)
+    {
+      NDB_MEMSET(&values_[0], 0, sizeof(values_));
+    }
+
+#if 0
+    void
+    sanity_check() const
+    {
+      INVARIANT(count_ <= ARRAY_NELEMS(values_));
+      if (!count_)
+        return;
+      uint32_t last = values_[0];
+      INVARIANT(last >= range_begin && last < range_end);
+      for (size_t i = 1; i < count_; last = values_[i], i++) {
+        INVARIANT(last < values_[i]);
+        INVARIANT(values_[i] >= range_begin && values_[i] < range_end);
+      }
+    }
+#else
+    inline void sanity_check() const {}
+#endif
+
+    // assumes list is in ascending order, no dups, and there is space
+    // remaining
+    //
+    // inserts in ascending order
+    void
+    insert(uint32_t v)
+    {
+      if (count_ >= ARRAY_NELEMS(values_)) {
+        cerr << "ERROR when trying to insert " << v
+             << ": " << vectorize() << endl;
+      }
+
+      INVARIANT(count_ < ARRAY_NELEMS(values_));
+      if (!count_ || values_[count_ - 1] < v) {
+        values_[count_++] = v;
+        sanity_check();
+        INVARIANT(contains(v));
+        return;
+      }
+      for (size_t i = 0; i < count_; i++) {
+        if (values_[i] > v) {
+          // move values_[i, count_) into slots values_[i+1, count_+1)
+          memmove(&values_[i+1], &values_[i], (count_ - i) * sizeof(uint32_t));
+          values_[i] = v;
+          count_++;
+          sanity_check();
+          INVARIANT(contains(v));
+          return;
+        }
+      }
+    }
+
+    inline bool
+    contains(uint32_t v) const
+    {
+      for (size_t i = 0; i < count_; i++)
+        if (values_[i] == v)
+          return true;
+      return false;
+    }
+
+    // removes v from list, returns true if actual removal happened.
+    // assumes v is in ascending order with no dups
+    bool
+    remove(uint32_t v)
+    {
+      for (size_t i = 0; i < count_; i++) {
+        if (values_[i] == v) {
+          // move values_[i+1, count_) into slots values_[i, count_-1)
+          memmove(&values_[i], &values_[i+1], (count_ - (i+1)) * sizeof(uint32_t));
+          count_ -= 1;
+          sanity_check();
+          INVARIANT(!contains(v));
+          // no dups assumption
+          return true;
+        } else if (values_[i] > v) {
+          // ascending order assumption
+          break;
+        }
+      }
+      INVARIANT(!contains(v));
+      return false;
+    }
+
+    inline vector<uint32_t>
+    vectorize() const
+    {
+      vector<uint32_t> v;
+      for (size_t i = 0; i < count_; i++)
+        v.push_back(values_[i]);
+      return v;
+    }
+  };
+
+  static volatile bool running = true;
+
+  // expects the values to be monotonically increasing (as records)
+  template <template <typename> class Protocol>
+  class counting_scan_callback : public txn_btree<Protocol>::search_range_callback {
+  public:
+    virtual bool
+    invoke(const typename txn_btree<Protocol>::keystring_type &k,
+           const string &v)
+    {
+      ALWAYS_ASSERT(k.length() == 8);
+      const uint64_t u64k =
+        host_endian_trfm<uint64_t>()(*reinterpret_cast<const uint64_t *>(k.data()));
+      if (v.size() != sizeof(rec)) {
+        cerr << "v.size(): " << v.size() << endl;
+        cerr << "sizeof rec: " << sizeof(rec) << endl;
+        ALWAYS_ASSERT(false);
+      }
+      const rec *r = (const rec *) v.data();
+      ALWAYS_ASSERT(u64k == r->v);
+      VERBOSE(cerr << "counting_scan_callback: " << hexify(k) << " => " << r->v << endl);
+      values_.push_back(r->v);
+      return true;
+    }
+    vector<uint32_t> values_;
+  };
+
+  template <template <typename> class TxnType, typename Traits>
+  class mutate_worker : public txn_btree_worker<TxnType> {
+  public:
+    mutate_worker(txn_btree<TxnType> &btr, uint64_t flags)
+      : txn_btree_worker<TxnType>(btr, flags), naborts(0) {}
+    virtual void run()
+    {
+      while (running) {
+        for (size_t i = range_begin; running && i < range_end; i++) {
+        retry:
+          typename Traits::StringAllocator arena;
+          //bool did_remove = false;
+          //uint64_t did_v = 0;
+          {
+            TxnType<Traits> t(this->txn_flags, arena);
+            try {
+              control_rec ctr_rec;
+              string v, v_ctr;
+              ALWAYS_ASSERT_COND_IN_TXN(t, this->btr->search(t, u64_varkey(ctr_key), v_ctr));
+              ALWAYS_ASSERT_COND_IN_TXN(t, v_ctr.size() == sizeof(control_rec));
+              ALWAYS_ASSERT_COND_IN_TXN(t, ((const control_rec *) v_ctr.data())->count_ > 1);
+              ctr_rec = *((const control_rec *) v_ctr.data());
+              if (this->btr->search(t, u64_varkey(i), v)) {
+                AssertByteEquality(rec(i), v);
+                this->btr->remove(t, u64_varkey(i));
+                ALWAYS_ASSERT_COND_IN_TXN(t, ctr_rec.remove(i));
+                //did_remove = true;
+              } else {
+                this->btr->insert_object(t, u64_varkey(i), rec(i));
+                ctr_rec.insert(i);
+              }
+              //did_v = ctr_rec.v;
+              ctr_rec.sanity_check();
+              this->btr->insert_object(t, u64_varkey(ctr_key), ctr_rec);
+              t.commit(true);
+            } catch (transaction_abort_exception &e) {
+              naborts++;
+              goto retry;
+            }
+          }
+
+          //{
+          //  TxnType<Traits> t(this->txn_flags, arena);
+          //  try {
+          //    string v, v_ctr;
+          //    ALWAYS_ASSERT_COND_IN_TXN(t, this->btr->search(t, u64_varkey(ctr_key), v_ctr));
+          //    ALWAYS_ASSERT_COND_IN_TXN(t, v_ctr.size() == sizeof(control_rec));
+          //    const bool ret = this->btr->search(t, u64_varkey(i), v);
+          //    t.commit(true);
+          //    if (reinterpret_cast<const rec *>(v_ctr.data())->v != did_v) {
+          //      cerr << "rec.v: " << reinterpret_cast<const rec *>(v_ctr.data())->v << ", did_v: " << did_v << endl;
+          //      ALWAYS_ASSERT(false);
+          //    }
+          //    if (did_remove && ret) {
+          //      cerr << "removed previous, but still found" << endl;
+          //      ALWAYS_ASSERT(false);
+          //    } else if (!did_remove && !ret) {
+          //      cerr << "did not previous, but not found" << endl;
+          //      ALWAYS_ASSERT(false);
+          //    }
+          //  } catch (transaction_abort_exception &e) {
+          //    // possibly aborts due to GC mechanism- if so, just move on
+          //  }
+          //}
+        }
+      }
+    }
+    size_t naborts;
+  };
+
+  template <template <typename> class TxnType, typename Traits>
+  class reader_worker : public txn_btree_worker<TxnType> {
+  public:
+    reader_worker(txn_btree<TxnType> &btr, uint64_t flags, bool reverse)
+      : txn_btree_worker<TxnType>(btr, flags),
+        reverse_(reverse), validations(0), naborts(0) {}
+    virtual void run()
+    {
+      while (running) {
+        typename Traits::StringAllocator arena;
+        TxnType<Traits> t(this->txn_flags, arena);
+        try {
+          string v_ctr;
+          ALWAYS_ASSERT_COND_IN_TXN(t, this->btr->search(t, u64_varkey(ctr_key), v_ctr));
+          ALWAYS_ASSERT_COND_IN_TXN(t, v_ctr.size() == sizeof(control_rec));
+          counting_scan_callback<TxnType> c;
+          if (!reverse_) {
+            const u64_varkey kend(range_end);
+            this->btr->search_range_call(t, u64_varkey(range_begin), &kend, c);
+          } else {
+            const u64_varkey mkey(range_begin - 1);
+            this->btr->rsearch_range_call(t, u64_varkey(range_end), &mkey, c);
+            // reverse values
+            c.values_ = vector<uint32_t>(c.values_.rbegin(), c.values_.rend());
+          }
+          t.commit(true);
+
+          const control_rec *crec = (const control_rec *) v_ctr.data();
+          crec->sanity_check();
+          auto cvec = crec->vectorize();
+          if (c.values_ != cvec) {
+            cerr << "observed (" << c.values_.size() << "): " << c.values_ << endl;
+            cerr << "db value (" << cvec.size() << "): " << cvec << endl;
+            ALWAYS_ASSERT_COND_IN_TXN(t, c.values_ == cvec);
+          }
+          validations++;
+          VERBOSE(cerr << "successful validation" << endl);
+        } catch (transaction_abort_exception &e) {
+          naborts++;
+          if (this->txn_flags & transaction_base::TXN_FLAG_READ_ONLY)
+            // RO txns shouldn't abort
+            ALWAYS_ASSERT_COND_IN_TXN(t, false);
+        }
+      }
+    }
+    bool reverse_;
+    size_t validations;
+    size_t naborts;
+  };
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+mp_test2()
+{
+  using namespace mp_test2_ns;
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      control_rec ctrl;
+      for (size_t i = range_begin; i < range_end; i++)
+        if ((i % 2) == 0) {
+          btr.insert_object(t, u64_varkey(i), rec(i));
+          ctrl.values_[ctrl.count_++] = i;
+        }
+      ctrl.sanity_check();
+      btr.insert_object(t, u64_varkey(ctr_key), ctrl);
+      AssertSuccessfulCommit(t);
+    }
+
+    // XXX(stephentu): HACK! we need to wait for the GC to
+    // compute a new consistent snapshot version that includes this
+    // latest update
+    txn_epoch_sync<TxnType>::sync();
+
+    {
+      // make sure the first validation passes
+      TxnType<Traits> t(
+          txn_flags | transaction_base::TXN_FLAG_READ_ONLY, arena);
+      typename Traits::StringAllocator arena;
+      string v_ctr;
+      ALWAYS_ASSERT_COND_IN_TXN(t, btr.search(t, u64_varkey(ctr_key), v_ctr));
+      ALWAYS_ASSERT_COND_IN_TXN(t, v_ctr.size() == sizeof(control_rec));
+      counting_scan_callback<TxnType> c;
+      const u64_varkey kend(range_end);
+      btr.search_range_call(t, u64_varkey(range_begin), &kend, c);
+      AssertSuccessfulCommit(t);
+
+      const control_rec *crec = (const control_rec *) v_ctr.data();
+      crec->sanity_check();
+      auto cvec = crec->vectorize();
+      if (c.values_ != cvec) {
+        cerr << "observed: " << c.values_ << endl;
+        cerr << "db value: " << cvec << endl;
+        ALWAYS_ASSERT_COND_IN_TXN(t, c.values_ == cvec);
+      }
+      VERBOSE(cerr << "initial read only scan passed" << endl);
+    }
+
+    mutate_worker<TxnType, Traits> w0(btr, txn_flags);
+    //reader_worker<TxnType, Traits> w1(btr, txn_flags);
+    reader_worker<TxnType, Traits> w2(btr, txn_flags | transaction_base::TXN_FLAG_READ_ONLY, false);
+    reader_worker<TxnType, Traits> w3(btr, txn_flags | transaction_base::TXN_FLAG_READ_ONLY, false);
+#ifdef HAVE_REVERSE_RANGE_SCANS
+    reader_worker<TxnType, Traits> w4(btr, txn_flags | transaction_base::TXN_FLAG_READ_ONLY, true);
+#else
+    reader_worker<TxnType, Traits> w4(btr, txn_flags | transaction_base::TXN_FLAG_READ_ONLY, false);
+#endif
+
+    running = true;
+    __sync_synchronize();
+    w0.start();
+    //w1.start();
+    w2.start();
+    w3.start();
+    w4.start();
+
+    sleep(30);
+    running = false;
+    __sync_synchronize();
+    w0.join();
+    //w1.join();
+    w2.join();
+    w3.join();
+    w4.join();
+
+    cerr << "mutate naborts: " << w0.naborts << endl;
+    //cerr << "reader naborts: " << w1.naborts << endl;
+    //cerr << "reader validations: " << w1.validations << endl;
+    cerr << "read-only reader 1 naborts: " << w2.naborts << endl;
+    cerr << "read-only reader 1 validations: " << w2.validations << endl;
+    cerr << "read-only reader 2 naborts: " << w3.naborts << endl;
+    cerr << "read-only reader 2 validations: " << w3.validations << endl;
+    cerr << "read-only reader 3 naborts: " << w4.naborts << endl;
+    cerr << "read-only reader 3 validations: " << w4.validations << endl;
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+namespace mp_test3_ns {
+
+  static const size_t amount_per_person = 100;
+  static const size_t naccounts = 100;
+  static const size_t niters = 1000000;
+
+  template <template <typename> class TxnType, typename Traits>
+  class transfer_worker : public txn_btree_worker<TxnType> {
+  public:
+    transfer_worker(txn_btree<TxnType> &btr, uint64_t flags, unsigned long seed)
+      : txn_btree_worker<TxnType>(btr, flags), seed(seed) {}
+    virtual void run()
+    {
+      fast_random r(seed);
+      for (size_t i = 0; i < niters; i++) {
+      retry:
+        try {
+          typename Traits::StringAllocator arena;
+          TxnType<Traits> t(this->txn_flags, arena);
+          uint64_t a = r.next() % naccounts;
+          uint64_t b = r.next() % naccounts;
+          while (unlikely(a == b))
+            b = r.next() % naccounts;
+          string arecv, brecv;
+          ALWAYS_ASSERT_COND_IN_TXN(t, this->btr->search(t, u64_varkey(a), arecv));
+          ALWAYS_ASSERT_COND_IN_TXN(t, this->btr->search(t, u64_varkey(b), brecv));
+          const rec *arec = (const rec *) arecv.data();
+          const rec *brec = (const rec *) brecv.data();
+          if (arec->v == 0) {
+            t.abort();
+          } else {
+            const uint64_t xfer = (arec->v > 1) ? (r.next() % (arec->v - 1) + 1) : 1;
+            this->btr->insert_object(t, u64_varkey(a), rec(arec->v - xfer));
+            this->btr->insert_object(t, u64_varkey(b), rec(brec->v + xfer));
+            t.commit(true);
+          }
+        } catch (transaction_abort_exception &e) {
+          goto retry;
+        }
+      }
+    }
+  private:
+    const unsigned long seed;
+  };
+
+  template <template <typename> class TxnType, typename Traits>
+  class invariant_worker_scan : public txn_btree_worker<TxnType>,
+                                public txn_btree<TxnType>::search_range_callback {
+  public:
+    invariant_worker_scan(txn_btree<TxnType> &btr, uint64_t flags)
+      : txn_btree_worker<TxnType>(btr, flags), running(true),
+        validations(0), naborts(0), sum(0) {}
+    virtual void run()
+    {
+      while (running) {
+        try {
+          typename Traits::StringAllocator arena;
+          TxnType<Traits> t(this->txn_flags, arena);
+          sum = 0;
+          this->btr->search_range_call(t, u64_varkey(0), NULL, *this);
+          t.commit(true);
+          ALWAYS_ASSERT_COND_IN_TXN(t, sum == (naccounts * amount_per_person));
+          validations++;
+        } catch (transaction_abort_exception &e) {
+          naborts++;
+        }
+      }
+    }
+    virtual bool invoke(const typename txn_btree<TxnType>::keystring_type &k,
+                        const string &v)
+    {
+      sum += ((rec *) v.data())->v;
+      return true;
+    }
+    volatile bool running;
+    size_t validations;
+    size_t naborts;
+    uint64_t sum;
+  };
+
+  template <template <typename> class TxnType, typename Traits>
+  class invariant_worker_1by1 : public txn_btree_worker<TxnType> {
+  public:
+    invariant_worker_1by1(txn_btree<TxnType> &btr, uint64_t flags)
+      : txn_btree_worker<TxnType>(btr, flags), running(true),
+        validations(0), naborts(0) {}
+    virtual void run()
+    {
+      while (running) {
+        try {
+          typename Traits::StringAllocator arena;
+          TxnType<Traits> t(this->txn_flags, arena);
+          uint64_t sum = 0;
+          for (uint64_t i = 0; i < naccounts; i++) {
+            string v;
+            ALWAYS_ASSERT_COND_IN_TXN(t, this->btr->search(t, u64_varkey(i), v));
+            sum += ((const rec *) v.data())->v;
+          }
+          t.commit(true);
+          if (sum != (naccounts * amount_per_person)) {
+            cerr << "sum: " << sum << endl;
+            cerr << "naccounts * amount_per_person: " << (naccounts * amount_per_person) << endl;
+          }
+          ALWAYS_ASSERT_COND_IN_TXN(t, sum == (naccounts * amount_per_person));
+          validations++;
+        } catch (transaction_abort_exception &e) {
+          naborts++;
+        }
+      }
+    }
+    volatile bool running;
+    size_t validations;
+    size_t naborts;
+  };
+
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+mp_test3()
+{
+  using namespace mp_test3_ns;
+
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      for (uint64_t i = 0; i < naccounts; i++)
+        btr.insert_object(t, u64_varkey(i), rec(amount_per_person));
+      AssertSuccessfulCommit(t);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+
+    transfer_worker<TxnType, Traits> w0(btr, txn_flags, 342),
+                             w1(btr, txn_flags, 93852),
+                             w2(btr, txn_flags, 23085),
+                             w3(btr, txn_flags, 859438989);
+    invariant_worker_scan<TxnType, Traits> w4(btr, txn_flags);
+    invariant_worker_1by1<TxnType, Traits> w5(btr, txn_flags);
+    invariant_worker_scan<TxnType, Traits> w6(btr, txn_flags | transaction_base::TXN_FLAG_READ_ONLY);
+    invariant_worker_1by1<TxnType, Traits> w7(btr, txn_flags | transaction_base::TXN_FLAG_READ_ONLY);
+
+    w0.start(); w1.start(); w2.start(); w3.start(); w4.start(); w5.start(); w6.start(); w7.start();
+    w0.join(); w1.join(); w2.join(); w3.join();
+    w4.running = false; w5.running = false; w6.running = false; w7.running = false;
+    __sync_synchronize();
+    w4.join(); w5.join(); w6.join(); w7.join();
+
+    cerr << "scan validations: " << w4.validations << ", scan aborts: " << w4.naborts << endl;
+    cerr << "1by1 validations: " << w5.validations << ", 1by1 aborts: " << w5.naborts << endl;
+    cerr << "scan-readonly validations: " << w6.validations << ", scan-readonly aborts: " << w6.naborts << endl;
+    cerr << "1by1-readonly validations: " << w7.validations << ", 1by1-readonly aborts: " << w7.naborts << endl;
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+namespace mp_test_simple_write_skew_ns {
+  static const size_t NDoctors = 16;
+
+  volatile bool running = true;
+
+  template <template <typename> class TxnType, typename Traits>
+  class get_worker : public txn_btree_worker<TxnType> {
+  public:
+    get_worker(unsigned int d, txn_btree<TxnType> &btr, uint64_t txn_flags)
+      : txn_btree_worker<TxnType>(btr, txn_flags), n(0), d(d) {}
+    virtual void run()
+    {
+      while (running) {
+        try {
+          typename Traits::StringAllocator arena;
+          TxnType<Traits> t(this->txn_flags, arena);
+          if ((n % 2) == 0) {
+            // try to take this doctor off call
+            unsigned int ctr = 0;
+            for (unsigned int i = 0; i < NDoctors && ctr < 2; i++) {
+              string v;
+              ALWAYS_ASSERT_COND_IN_TXN(t, this->btr->search(t, u64_varkey(i), v));
+              INVARIANT(v.size() == sizeof(rec));
+              const rec *r = (const rec *) v.data();
+              if (r->v)
+                ctr++;
+            }
+            if (ctr == 2)
+              this->btr->insert_object(t, u64_varkey(d), rec(0));
+            t.commit(true);
+            ALWAYS_ASSERT_COND_IN_TXN(t, ctr >= 1);
+          } else {
+            // place this doctor on call
+            this->btr->insert_object(t, u64_varkey(d), rec(1));
+            t.commit(true);
+          }
+          n++;
+        } catch (transaction_abort_exception &e) {
+          // no-op
+        }
+      }
+    }
+    uint64_t n;
+  private:
+    unsigned int d;
+  };
+
+  template <template <typename> class TxnType, typename Traits>
+  class scan_worker : public txn_btree_worker<TxnType>,
+                      public txn_btree<TxnType>::search_range_callback {
+  public:
+    scan_worker(unsigned int d, txn_btree<TxnType> &btr, uint64_t txn_flags)
+      : txn_btree_worker<TxnType>(btr, txn_flags), n(0), d(d), ctr(0) {}
+    virtual void run()
+    {
+      while (running) {
+        try {
+          typename Traits::StringAllocator arena;
+          TxnType<Traits> t(this->txn_flags, arena);
+          if ((n % 2) == 0) {
+            ctr = 0;
+            this->btr->search_range_call(t, u64_varkey(0), NULL, *this);
+            if (ctr == 2)
+              this->btr->insert_object(t, u64_varkey(d), rec(0));
+            t.commit(true);
+            ALWAYS_ASSERT_COND_IN_TXN(t, ctr >= 1);
+          } else {
+            this->btr->insert_object(t, u64_varkey(d), rec(1));
+            t.commit(true);
+          }
+          n++;
+        } catch (transaction_abort_exception &e) {
+          // no-op
+        }
+      }
+    }
+    virtual bool invoke(const typename txn_btree<TxnType>::keystring_type &k,
+                        const string &v)
+    {
+      INVARIANT(v.size() == sizeof(rec));
+      const rec *r = (const rec *) v.data();
+      if (r->v)
+        ctr++;
+      return ctr < 2;
+    }
+    uint64_t n;
+  private:
+    unsigned int d;
+    unsigned int ctr;
+  };
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+mp_test_simple_write_skew()
+{
+  using namespace mp_test_simple_write_skew_ns;
+
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+
+    txn_btree<TxnType> btr;
+    typename Traits::StringAllocator arena;
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      static_assert(NDoctors >= 2, "XX");
+      for (uint64_t i = 0; i < NDoctors; i++)
+        btr.insert_object(t, u64_varkey(i), rec(i < 2 ? 1 : 0));
+      AssertSuccessfulCommit(t);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+
+    running = true;
+    __sync_synchronize();
+    vector<txn_btree_worker<TxnType> *> workers;
+    for (size_t i = 0; i < NDoctors / 2; i++)
+      workers.push_back(new get_worker<TxnType, Traits>(i, btr, txn_flags));
+    for (size_t i = NDoctors / 2; i < NDoctors; i++)
+      workers.push_back(new scan_worker<TxnType, Traits>(i, btr, txn_flags));
+    for (size_t i = 0; i < NDoctors; i++)
+      workers[i]->start();
+    sleep(10);
+    running = false;
+    __sync_synchronize();
+
+
+    size_t n_get_succ = 0, n_scan_succ = 0;
+    for (size_t i = 0; i < NDoctors; i++) {
+      workers[i]->join();
+      if (get_worker<TxnType, Traits> *w = dynamic_cast<get_worker<TxnType, Traits> *>(workers[i]))
+        n_get_succ += w->n;
+      else if (scan_worker<TxnType, Traits> *w = dynamic_cast<scan_worker<TxnType, Traits> *>(workers[i]))
+        n_scan_succ += w->n;
+      else
+        ALWAYS_ASSERT(false);
+      delete workers[i];
+    }
+    workers.clear();
+
+    cerr << "get_worker  txns: " << n_get_succ << endl;
+    cerr << "scan_worker txns: " << n_scan_succ << endl;
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+namespace mp_test_batch_processing_ns {
+
+  volatile bool running = true;
+
+  static inline string
+  MakeKey(uint32_t batch_id, uint32_t receipt_id)
+  {
+    const big_endian_trfm<int32_t> t;
+    string buf(8, 0);
+    uint32_t *p = (uint32_t *) &buf[0];
+    *p++ = t(batch_id);
+    *p++ = t(receipt_id);
+    return buf;
+  }
+
+  template <template <typename> class TxnType, typename Traits>
+  class report_worker : public ndb_thread,
+                        public txn_btree<TxnType>::search_range_callback {
+  public:
+    report_worker(txn_btree<TxnType> &ctrl, txn_btree<TxnType> &receipts, uint64_t txn_flags)
+      : ctrl(&ctrl), receipts(&receipts), txn_flags(txn_flags), n(0), m(0), sum(0) {}
+    virtual void run()
+    {
+      while (running) {
+        try {
+          typename Traits::StringAllocator arena;
+          TxnType<Traits> t(this->txn_flags, arena);
+          string v;
+          ALWAYS_ASSERT_COND_IN_TXN(t, ctrl->search(t, u64_varkey(0), v));
+          ALWAYS_ASSERT_COND_IN_TXN(t, v.size() == sizeof(rec));
+          const rec * const r = (const rec *) v.data();
+          const uint32_t prev_bid = r->v - 1; // prev batch
+          sum = 0;
+          const string endkey = MakeKey(prev_bid, numeric_limits<uint32_t>::max());
+          receipts->search_range_call(t, MakeKey(prev_bid, 0), &endkey, *this);
+          t.commit(true);
+          map<uint32_t, uint32_t>::iterator it = reports.find(prev_bid);
+          if (it != reports.end()) {
+            ALWAYS_ASSERT_COND_IN_TXN(t, sum == it->second);
+            m++;
+          } else {
+            reports[prev_bid] = sum;
+          }
+          n++;
+        } catch (transaction_abort_exception &e) {
+          // no-op
+        }
+      }
+    }
+    virtual bool invoke(const typename txn_btree<TxnType>::keystring_type &k,
+                        const string &v)
+    {
+      INVARIANT(v.size() == sizeof(rec));
+      const rec * const r = (const rec *) v.data();
+      sum += r->v;
+      return true;
+    }
+  private:
+    txn_btree<TxnType> *ctrl;
+    txn_btree<TxnType> *receipts;
+    uint64_t txn_flags;
+
+  public:
+    uint64_t n;
+    uint64_t m;
+
+  private:
+    map<uint32_t, uint32_t> reports;
+    unsigned int sum;
+  };
+
+  template <template <typename> class TxnType, typename Traits>
+  class new_receipt_worker : public ndb_thread {
+  public:
+    new_receipt_worker(txn_btree<TxnType> &ctrl, txn_btree<TxnType> &receipts, uint64_t txn_flags)
+      : ctrl(&ctrl), receipts(&receipts), txn_flags(txn_flags),
+        n(0), last_bid(0), last_rid(0) {}
+    virtual void run()
+    {
+      while (running) {
+        try {
+          typename Traits::StringAllocator arena;
+          TxnType<Traits> t(this->txn_flags, arena);
+          string v;
+          ALWAYS_ASSERT_COND_IN_TXN(t, ctrl->search(t, u64_varkey(0), v));
+          ALWAYS_ASSERT_COND_IN_TXN(t, v.size() == sizeof(rec));
+          const rec * const r = (const rec *) v.data();
+          const uint32_t cur_bid = r->v;
+          const uint32_t cur_rid = (cur_bid != last_bid) ? 0 : last_rid + 1;
+          const string rkey = MakeKey(cur_bid, cur_rid);
+          receipts->insert_object(t, rkey, rec(1));
+          t.commit(true);
+          last_bid = cur_bid;
+          last_rid = cur_rid;
+          n++;
+        } catch (transaction_abort_exception &e) {
+          // no-op
+        }
+      }
+    }
+
+  private:
+    txn_btree<TxnType> *ctrl;
+    txn_btree<TxnType> *receipts;
+    uint64_t txn_flags;
+
+  public:
+    uint64_t n;
+
+  private:
+    uint32_t last_bid;
+    uint32_t last_rid;
+  };
+
+  template <template <typename> class TxnType, typename Traits>
+  class incr_worker : public ndb_thread {
+  public:
+    incr_worker(txn_btree<TxnType> &ctrl, txn_btree<TxnType> &receipts, uint64_t txn_flags)
+      : ctrl(&ctrl), receipts(&receipts), txn_flags(txn_flags), n(0) {}
+    virtual void run()
+    {
+      struct timespec t;
+      NDB_MEMSET(&t, 0, sizeof(t));
+      t.tv_nsec = 1000; // 1 us
+      while (running) {
+        try {
+          typename Traits::StringAllocator arena;
+          TxnType<Traits> t(this->txn_flags, arena);
+          string v;
+          ALWAYS_ASSERT_COND_IN_TXN(t, ctrl->search(t, u64_varkey(0), v));
+          ALWAYS_ASSERT_COND_IN_TXN(t, v.size() == sizeof(rec));
+          const rec * const r = (const rec *) v.data();
+          ctrl->insert_object(t, u64_varkey(0), rec(r->v + 1));
+          t.commit(true);
+          n++;
+        } catch (transaction_abort_exception &e) {
+          // no-op
+        }
+        nanosleep(&t, NULL);
+      }
+    }
+  private:
+    txn_btree<TxnType> *ctrl;
+    txn_btree<TxnType> *receipts;
+    uint64_t txn_flags;
+
+  public:
+    uint64_t n;
+  };
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+mp_test_batch_processing()
+{
+  using namespace mp_test_batch_processing_ns;
+
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+
+    txn_btree<TxnType> ctrl;
+    txn_btree<TxnType> receipts;
+    typename Traits::StringAllocator arena;
+    {
+      TxnType<Traits> t(txn_flags, arena);
+      ctrl.insert_object(t, u64_varkey(0), rec(1));
+      AssertSuccessfulCommit(t);
+    }
+
+    txn_epoch_sync<TxnType>::sync();
+
+    report_worker<TxnType, Traits> w0(ctrl, receipts, txn_flags);
+    new_receipt_worker<TxnType, Traits> w1(ctrl, receipts, txn_flags);
+    incr_worker<TxnType, Traits> w2(ctrl, receipts, txn_flags);
+    running = true;
+    __sync_synchronize();
+    w0.start(); w1.start(); w2.start();
+    sleep(10);
+    running = false;
+    __sync_synchronize();
+    w0.join(); w1.join(); w2.join();
+
+
+    cerr << "report_worker      txns       : " << w0.n << endl;
+    cerr << "report_worker      validations: " << w0.m << endl;
+    cerr << "new_receipt_worker txns       : " << w1.n << endl;
+    cerr << "incr_worker        txns       : " << w2.n << endl;
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+namespace read_only_perf_ns {
+  const size_t nkeys = 140000000; // 140M
+  //const size_t nkeys = 100000; // 100K
+
+  unsigned long seeds[] = {
+    9576455804445224191ULL,
+    3303315688255411629ULL,
+    3116364238170296072ULL,
+    641702699332002535ULL,
+    17755947590284612420ULL,
+    13349066465957081273ULL,
+    16389054441777092823ULL,
+    2687412585397891607ULL,
+    16665670053534306255ULL,
+    5166823197462453937ULL,
+    1252059952779729626ULL,
+    17962022827457676982ULL,
+    940911318964853784ULL,
+    479878990529143738ULL,
+    250864516707124695ULL,
+    8507722621803716653ULL,
+  };
+
+  volatile bool running = false;
+
+  template <template <typename> class TxnType, typename Traits>
+  class worker : public txn_btree_worker<TxnType> {
+  public:
+    worker(unsigned int seed, txn_btree<TxnType> &btr, uint64_t txn_flags)
+      : txn_btree_worker<TxnType>(btr, txn_flags), n(0), seed(seed) {}
+    virtual void run()
+    {
+      fast_random r(seed);
+      while (running) {
+        const uint64_t k = r.next() % nkeys;
+      retry:
+        try {
+          typename Traits::StringAllocator arena;
+          TxnType<Traits> t(this->txn_flags, arena);
+          string v;
+          bool found = this->btr->search(t, u64_varkey(k), v);
+          t.commit(true);
+          ALWAYS_ASSERT_COND_IN_TXN(t, found);
+          AssertByteEquality(rec(k + 1), v);
+        } catch (transaction_abort_exception &e) {
+          goto retry;
+        }
+        n++;
+      }
+    }
+    uint64_t n;
+  private:
+    unsigned int seed;
+  };
+}
+
+template <template <typename> class TxnType, typename Traits>
+static void
+read_only_perf()
+{
+  using namespace read_only_perf_ns;
+  for (size_t txn_flags_idx = 0;
+       txn_flags_idx < ARRAY_NELEMS(TxnFlags);
+       txn_flags_idx++) {
+    const uint64_t txn_flags = TxnFlags[txn_flags_idx];
+
+    txn_btree<TxnType> btr;
+
+    {
+      const size_t nkeyspertxn = 100000;
+      for (size_t i = 0; i < nkeys / nkeyspertxn; i++) {
+        TxnType<Traits> t;
+        const size_t end = (i == (nkeys / nkeyspertxn - 1)) ? nkeys : ((i + 1) * nkeyspertxn);
+        for (size_t j = i * nkeyspertxn; j < end; j++)
+          btr.insert_object(t, u64_varkey(j), rec(j + 1));
+        AssertSuccessfulCommit(t);
+        cerr << "batch " << i << " completed" << endl;
+      }
+      cerr << "btree loaded, test starting" << endl;
+    }
+
+    vector<worker<TxnType, Traits> *> workers;
+    for (size_t i = 0; i < ARRAY_NELEMS(seeds); i++)
+      workers.push_back(new worker<TxnType, Traits>(seeds[i], btr, txn_flags));
+
+    running = true;
+    timer t;
+    COMPILER_MEMORY_FENCE;
+    for (size_t i = 0; i < ARRAY_NELEMS(seeds); i++)
+      workers[i]->start();
+    sleep(30);
+    COMPILER_MEMORY_FENCE;
+    running = false;
+    COMPILER_MEMORY_FENCE;
+    uint64_t total_n = 0;
+    for (size_t i = 0; i < ARRAY_NELEMS(seeds); i++) {
+      workers[i]->join();
+      total_n += workers[i]->n;
+      delete workers[i];
+    }
+
+    double agg_throughput = double(total_n) / (double(t.lap()) / 1000000.0);
+    double avg_per_core_throughput = agg_throughput / double(ARRAY_NELEMS(seeds));
+
+    cerr << "agg_read_throughput: " << agg_throughput << " gets/sec" << endl;
+    cerr << "avg_per_core_read_throughput: " << avg_per_core_throughput << " gets/sec/core" << endl;
+
+    txn_epoch_sync<TxnType>::sync();
+    txn_epoch_sync<TxnType>::finish();
+  }
+}
+
+void txn_btree_test()
+{
+  cerr << "Test proto2" << endl;
+  test_typed_btree<transaction_proto2, default_stable_transaction_traits>();
+  test1<transaction_proto2, default_transaction_traits>();
+  test2<transaction_proto2, default_transaction_traits>();
+  test_absent_key_race<transaction_proto2, default_transaction_traits>();
+  test_inc_value_size<transaction_proto2, default_transaction_traits>();
+  test_multi_btree<transaction_proto2, default_transaction_traits>();
+  test_read_only_snapshot<transaction_proto2, default_transaction_traits>();
+  test_long_keys<transaction_proto2, default_transaction_traits>();
+  test_long_keys2<transaction_proto2, default_transaction_traits>();
+  test_insert_same_key<transaction_proto2, default_transaction_traits>();
+
+  //mp_stress_test_allocator<transaction_proto2, default_transaction_traits>();
+  mp_stress_test_insert_removes<transaction_proto2, default_transaction_traits>();
+  mp_test1<transaction_proto2, default_transaction_traits>();
+  mp_test2<transaction_proto2, default_transaction_traits>();
+  mp_test3<transaction_proto2, default_transaction_traits>();
+  mp_test_simple_write_skew<transaction_proto2, default_transaction_traits>();
+  mp_test_batch_processing<transaction_proto2, default_transaction_traits>();
+
+  //read_only_perf<transaction_proto1>();
+  //read_only_perf<transaction_proto2>();
+}
diff --git a/silo/txn_btree.h b/silo/txn_btree.h
new file mode 100644 (file)
index 0000000..e22b87e
--- /dev/null
@@ -0,0 +1,483 @@
+#ifndef _NDB_TXN_BTREE_H_
+#define _NDB_TXN_BTREE_H_
+
+#include "base_txn_btree.h"
+
+// XXX: hacky
+extern void txn_btree_test();
+
+struct txn_btree_ {
+  class key_reader {
+  public:
+    inline ALWAYS_INLINE const std::string &
+    operator()(const std::string &s)
+    {
+      return s;
+    }
+#if NDB_MASSTREE
+    inline ALWAYS_INLINE lcdf::Str operator()(lcdf::Str s) {
+      return s;
+    }
+#endif
+  };
+
+  class key_writer {
+  public:
+    constexpr key_writer(const std::string *k)
+      : k(k) {}
+
+    template <typename StringAllocator>
+    inline const std::string *
+    fully_materialize(bool stable_input, StringAllocator &sa)
+    {
+      if (stable_input || !k)
+        return k;
+      std::string * const ret = sa();
+      ret->assign(k->data(), k->size());
+      return ret;
+    }
+
+  private:
+    const std::string *k;
+  };
+
+  // does not bother to interpret the bytes from a record
+  class single_value_reader {
+  public:
+    typedef std::string value_type;
+
+    constexpr single_value_reader(std::string *px, size_t max_bytes_read)
+      : px(px), max_bytes_read(max_bytes_read) {}
+
+    template <typename StringAllocator>
+    inline bool
+    operator()(const uint8_t *data, size_t sz, StringAllocator &sa)
+    {
+      const size_t readsz = std::min(sz, max_bytes_read);
+      px->assign((const char *) data, readsz);
+      return true;
+    }
+
+    inline std::string &
+    results()
+    {
+      return *px;
+    }
+
+    inline const std::string &
+    results() const
+    {
+      return *px;
+    }
+
+    template <typename StringAllocator>
+    inline void
+    dup(const std::string &vdup, StringAllocator &sa)
+    {
+      *px = vdup;
+    }
+
+  private:
+    std::string *px;
+    size_t max_bytes_read;
+  };
+
+  class value_reader {
+  public:
+    typedef std::string value_type;
+
+    constexpr value_reader(size_t max_bytes_read)
+      : px(nullptr), max_bytes_read(max_bytes_read) {}
+
+    template <typename StringAllocator>
+    inline bool
+    operator()(const uint8_t *data, size_t sz, StringAllocator &sa)
+    {
+      px = sa();
+      const size_t readsz = std::min(sz, max_bytes_read);
+      px->assign((const char *) data, readsz);
+      return true;
+    }
+
+    inline std::string &
+    results()
+    {
+      return *px;
+    }
+
+    inline const std::string &
+    results() const
+    {
+      return *px;
+    }
+
+    template <typename StringAllocator>
+    inline void
+    dup(const std::string &vdup, StringAllocator &sa)
+    {
+      px = sa();
+      *px = vdup;
+    }
+
+  private:
+    std::string *px;
+    size_t max_bytes_read;
+  };
+
+  class value_writer {
+  public:
+    constexpr value_writer(const std::string *v) : v(v) {}
+    inline size_t
+    compute_needed(const uint8_t *buf, size_t sz)
+    {
+      return v ? v->size() : 0;
+    }
+    template <typename StringAllocator>
+    inline const std::string *
+    fully_materialize(bool stable_input, StringAllocator &sa)
+    {
+      if (stable_input || !v)
+        return v;
+      std::string * const ret = sa();
+      ret->assign(v->data(), v->size());
+      return ret;
+    }
+
+    // input [buf, buf+sz) is old value
+    inline void
+    operator()(uint8_t *buf, size_t sz)
+    {
+      if (!v)
+        return;
+      NDB_MEMCPY(buf, v->data(), v->size());
+    }
+  private:
+    const std::string *v;
+  };
+
+  static size_t
+  tuple_writer(dbtuple::TupleWriterMode mode, const void *v, uint8_t *p, size_t sz)
+  {
+    const std::string * const vx = reinterpret_cast<const std::string *>(v);
+    switch (mode) {
+    case dbtuple::TUPLE_WRITER_NEEDS_OLD_VALUE:
+      return 0;
+    case dbtuple::TUPLE_WRITER_COMPUTE_NEEDED:
+    case dbtuple::TUPLE_WRITER_COMPUTE_DELTA_NEEDED:
+      return vx->size();
+    case dbtuple::TUPLE_WRITER_DO_WRITE:
+    case dbtuple::TUPLE_WRITER_DO_DELTA_WRITE:
+      NDB_MEMCPY(p, vx->data(), vx->size());
+      return 0;
+    }
+    ALWAYS_ASSERT(false);
+    return 0;
+  }
+
+  typedef std::string Key;
+  typedef key_reader KeyReader;
+  typedef key_writer KeyWriter;
+  typedef std::string Value;
+  typedef single_value_reader SingleValueReader;
+  typedef value_reader ValueReader;
+  typedef value_writer ValueWriter;
+};
+
+/**
+ * This class implements a serializable, multi-version b-tree
+ *
+ * It presents mostly same interface as the underlying concurrent b-tree,
+ * but the interface is serializable. The main interface differences are,
+ * insert() and put() do not return a boolean value to indicate whether or not
+ * they caused the tree to mutate
+ *
+ * A txn_btree does not allow keys to map to NULL records, even though the
+ * underlying concurrent btree does- this simplifies some of the book-keeping
+ * Additionally, keys cannot map to zero length records.
+ *
+ * Note that the txn_btree *manages* the memory of both keys and values internally.
+ * See the specific notes on search()/insert() about memory ownership
+ */
+template <template <typename> class Transaction>
+class txn_btree : public base_txn_btree<Transaction, txn_btree_> {
+  typedef base_txn_btree<Transaction, txn_btree_> super_type;
+public:
+
+  //template <typename Traits>
+  //struct transaction {
+  //  typedef Transaction<txn_btree_, Traits> type;
+  //};
+
+  //template <typename Traits>
+  //  using transaction = Transaction<txn_btree_, Traits>;
+
+  typedef typename super_type::string_type string_type;
+  typedef typename super_type::keystring_type keystring_type;
+  typedef typename super_type::size_type size_type;
+
+  typedef txn_btree_::Key key_type;
+  typedef txn_btree_::Value value_type;
+  typedef txn_btree_::KeyReader key_reader_type;
+  typedef txn_btree_::KeyWriter key_writer_type;
+  typedef txn_btree_::SingleValueReader single_value_reader_type;
+  typedef txn_btree_::ValueReader value_reader_type;
+  typedef txn_btree_::ValueWriter value_writer_type;
+
+  struct search_range_callback {
+  public:
+    virtual ~search_range_callback() {}
+    virtual bool invoke(const keystring_type &k, const string_type &v) = 0;
+  };
+
+private:
+
+  template <typename T>
+  class type_callback_wrapper : public search_range_callback {
+  public:
+    constexpr type_callback_wrapper(T *callback)
+      : callback(callback) {}
+    virtual bool
+    invoke(const keystring_type &k, const string_type &v)
+    {
+      return callback->operator()(k, v);
+    }
+  private:
+    T *const callback;
+  };
+
+  static inline ALWAYS_INLINE string_type
+  to_string_type(const varkey &k)
+  {
+    return string_type((const char *) k.data(), k.size());
+  }
+
+  template <typename Traits>
+  static inline const std::string *
+  stablize(Transaction<Traits> &t, const std::string &s)
+  {
+    if (Traits::stable_input_memory)
+      return &s;
+    std::string * const px = t.string_allocator()();
+    *px = s;
+    return px;
+  }
+
+  template <typename Traits>
+  static inline const std::string *
+  stablize(Transaction<Traits> &t, const uint8_t *p, size_t sz)
+  {
+    if (!sz)
+      return nullptr;
+    std::string * const px = t.string_allocator()();
+    px->assign((const char *) p, sz);
+    return px;
+  }
+
+  template <typename Traits>
+  static inline const std::string *
+  stablize(Transaction<Traits> &t, const varkey &k)
+  {
+    return stablize(t, k.data(), k.size());
+  }
+
+public:
+
+  txn_btree(size_type value_size_hint = 128,
+            bool mostly_append = false,
+            const std::string &name = "<unknown>")
+    : super_type(value_size_hint, mostly_append, name)
+  {}
+
+  template <typename Traits>
+  inline bool
+  search(Transaction<Traits> &t,
+         const varkey &k,
+         value_type &v,
+         size_t max_bytes_read = string_type::npos)
+  {
+    return search(t, to_string_type(k), v, max_bytes_read);
+  }
+
+  // either returns false or v is set to not-empty with value
+  // precondition: max_bytes_read > 0
+  template <typename Traits>
+  inline bool
+  search(Transaction<Traits> &t,
+         const key_type &k,
+         value_type &v,
+         size_type max_bytes_read = string_type::npos)
+  {
+    single_value_reader_type r(&v, max_bytes_read);
+    return this->do_search(t, k, r);
+  }
+
+  template <typename Traits>
+  inline void
+  search_range_call(Transaction<Traits> &t,
+                    const key_type &lower,
+                    const key_type *upper,
+                    search_range_callback &callback,
+                    size_type max_bytes_read = string_type::npos)
+  {
+    key_reader_type kr;
+    value_reader_type vr(max_bytes_read);
+    this->do_search_range_call(t, lower, upper, callback, kr, vr);
+  }
+
+  template <typename Traits>
+  inline void
+  rsearch_range_call(Transaction<Traits> &t,
+                     const key_type &upper,
+                     const key_type *lower,
+                     search_range_callback &callback,
+                     size_type max_bytes_read = string_type::npos)
+  {
+    key_reader_type kr;
+    value_reader_type vr(max_bytes_read);
+    this->do_rsearch_range_call(t, upper, lower, callback, kr, vr);
+  }
+
+  template <typename Traits>
+  inline void
+  search_range_call(Transaction<Traits> &t,
+                    const varkey &lower,
+                    const varkey *upper,
+                    search_range_callback &callback,
+                    size_type max_bytes_read = string_type::npos)
+  {
+    key_type u;
+    if (upper)
+      u = to_string_type(*upper);
+    search_range_call(t, to_string_type(lower),
+        upper ? &u : nullptr, callback, max_bytes_read);
+  }
+
+  template <typename Traits>
+  inline void
+  rsearch_range_call(Transaction<Traits> &t,
+                     const varkey &upper,
+                     const varkey *lower,
+                     search_range_callback &callback,
+                     size_type max_bytes_read = string_type::npos)
+  {
+    key_type l;
+    if (lower)
+      l = to_string_type(*lower);
+    rsearch_range_call(t, to_string_type(upper),
+        lower ? &l : nullptr, callback, max_bytes_read);
+  }
+
+  template <typename Traits, typename T>
+  inline void
+  search_range(Transaction<Traits> &t,
+               const key_type &lower,
+               const key_type *upper,
+               T &callback,
+               size_type max_bytes_read = string_type::npos)
+  {
+    type_callback_wrapper<T> w(&callback);
+    search_range_call(t, lower, upper, w, max_bytes_read);
+  }
+
+  template <typename Traits, typename T>
+  inline void
+  search_range(Transaction<Traits> &t,
+               const varkey &lower,
+               const varkey *upper,
+               T &callback,
+               size_type max_bytes_read = string_type::npos)
+  {
+    key_type u;
+    if (upper)
+      u = to_string_type(*upper);
+    search_range(t, to_string_type(lower),
+        upper ? &u : nullptr, callback, max_bytes_read);
+  }
+
+  template <typename Traits>
+  inline void
+  put(Transaction<Traits> &t, const key_type &k, const value_type &v)
+  {
+    INVARIANT(!v.empty());
+    this->do_tree_put(
+        t, stablize(t, k), stablize(t, v),
+        txn_btree_::tuple_writer, false);
+  }
+
+  template <typename Traits>
+  inline void
+  put(Transaction<Traits> &t, const varkey &k, const value_type &v)
+  {
+    INVARIANT(!v.empty());
+    this->do_tree_put(
+        t, stablize(t, k), stablize(t, v),
+        txn_btree_::tuple_writer, false);
+  }
+
+  template <typename Traits>
+  inline void
+  insert(Transaction<Traits> &t, const key_type &k, const value_type &v)
+  {
+    INVARIANT(!v.empty());
+    this->do_tree_put(
+        t, stablize(t, k), stablize(t, v),
+        txn_btree_::tuple_writer, true);
+  }
+
+  // insert() methods below are for legacy use
+
+  template <typename Traits>
+  inline void
+  insert(Transaction<Traits> &t, const key_type &k, const uint8_t *v, size_type sz)
+  {
+    INVARIANT(v);
+    INVARIANT(sz);
+    this->do_tree_put(
+        t, stablize(t, k), stablize(t, v, sz),
+        txn_btree_::tuple_writer, true);
+  }
+
+  template <typename Traits>
+  inline void
+  insert(Transaction<Traits> &t, const varkey &k, const uint8_t *v, size_type sz)
+  {
+    INVARIANT(v);
+    INVARIANT(sz);
+    this->do_tree_put(
+        t, stablize(t, k), stablize(t, v, sz),
+        txn_btree_::tuple_writer, true);
+  }
+
+  template <typename Traits, typename T>
+  inline void
+  insert_object(Transaction<Traits> &t, const varkey &k, const T &obj)
+  {
+    insert(t, k, (const uint8_t *) &obj, sizeof(obj));
+  }
+
+  template <typename Traits, typename T>
+  inline void
+  insert_object(Transaction<Traits> &t, const key_type &k, const T &obj)
+  {
+    insert(t, k, (const uint8_t *) &obj, sizeof(obj));
+  }
+
+  template <typename Traits>
+  inline void
+  remove(Transaction<Traits> &t, const key_type &k)
+  {
+    this->do_tree_put(t, stablize(t, k), nullptr, txn_btree_::tuple_writer, false);
+  }
+
+  template <typename Traits>
+  inline void
+  remove(Transaction<Traits> &t, const varkey &k)
+  {
+    this->do_tree_put(t, stablize(t, k), nullptr, txn_btree_::tuple_writer, false);
+  }
+
+  static void Test();
+
+};
+
+#endif /* _NDB_TXN_BTREE_H_ */
diff --git a/silo/txn_impl.h b/silo/txn_impl.h
new file mode 100644 (file)
index 0000000..07591b7
--- /dev/null
@@ -0,0 +1,643 @@
+#ifndef _NDB_TXN_IMPL_H_
+#define _NDB_TXN_IMPL_H_
+
+#include "txn.h"
+#include "lockguard.h"
+
+// base definitions
+
+template <template <typename> class Protocol, typename Traits>
+transaction<Protocol, Traits>::transaction(uint64_t flags, string_allocator_type &sa)
+  : transaction_base(flags), sa(&sa)
+{
+  INVARIANT(rcu::s_instance.in_rcu_region());
+#ifdef BTREE_LOCK_OWNERSHIP_CHECKING
+  concurrent_btree::NodeLockRegionBegin();
+#endif
+}
+
+template <template <typename> class Protocol, typename Traits>
+transaction<Protocol, Traits>::~transaction()
+{
+  // transaction shouldn't fall out of scope w/o resolution
+  // resolution means TXN_EMBRYO, TXN_COMMITED, and TXN_ABRT
+  INVARIANT(state != TXN_ACTIVE);
+  INVARIANT(rcu::s_instance.in_rcu_region());
+  const unsigned cur_depth = rcu_guard_->sync()->depth();
+  rcu_guard_.destroy();
+  if (cur_depth == 1) {
+    INVARIANT(!rcu::s_instance.in_rcu_region());
+    cast()->on_post_rcu_region_completion();
+  }
+#ifdef BTREE_LOCK_OWNERSHIP_CHECKING
+  concurrent_btree::AssertAllNodeLocksReleased();
+#endif
+}
+
+template <template <typename> class Protocol, typename Traits>
+void
+transaction<Protocol, Traits>::clear()
+{
+  // it's actually *more* efficient to not call clear explicitly on the
+  // read/write/absent sets, and let the destructors do the clearing- this is
+  // because the destructors can take shortcuts since it knows the obj doesn't
+  // have to end in a valid state
+}
+
+template <template <typename> class Protocol, typename Traits>
+void
+transaction<Protocol, Traits>::abort_impl(abort_reason reason)
+{
+  abort_trap(reason);
+  switch (state) {
+  case TXN_EMBRYO:
+  case TXN_ACTIVE:
+    break;
+  case TXN_ABRT:
+    return;
+  case TXN_COMMITED:
+    throw transaction_unusable_exception();
+  }
+  state = TXN_ABRT;
+  this->reason = reason;
+
+  // on abort, we need to go over all insert nodes and
+  // release the locks
+  typename write_set_map::iterator it     = write_set.begin();
+  typename write_set_map::iterator it_end = write_set.end();
+  for (; it != it_end; ++it) {
+    dbtuple * const tuple = it->get_tuple();
+    if (it->is_insert()) {
+      INVARIANT(tuple->is_locked());
+      this->cleanup_inserted_tuple_marker(tuple, it->get_key(), it->get_btree());
+      tuple->unlock();
+    }
+  }
+
+  clear();
+}
+
+template <template <typename> class Protocol, typename Traits>
+void
+transaction<Protocol, Traits>::cleanup_inserted_tuple_marker(
+    dbtuple *marker, const std::string &key, concurrent_btree *btr)
+{
+  // XXX: this code should really live in txn_proto2_impl.h
+  INVARIANT(marker->version == dbtuple::MAX_TID);
+  INVARIANT(marker->is_locked());
+  INVARIANT(marker->is_lock_owner());
+  typename concurrent_btree::value_type removed = 0;
+  const bool did_remove = btr->remove(varkey(key), &removed);
+  if (unlikely(!did_remove)) {
+#ifdef CHECK_INVARIANTS
+    std::cerr << " *** could not remove key: " << util::hexify(key)  << std::endl;
+#ifdef TUPLE_CHECK_KEY
+    std::cerr << " *** original key        : " << util::hexify(marker->key) << std::endl;
+#endif
+#endif
+    ALWAYS_ASSERT(false);
+  }
+  INVARIANT(removed == (typename concurrent_btree::value_type) marker);
+  INVARIANT(marker->is_latest());
+  marker->clear_latest();
+  dbtuple::release(marker); // rcu free
+}
+
+namespace {
+  inline const char *
+  transaction_state_to_cstr(transaction_base::txn_state state)
+  {
+    switch (state) {
+    case transaction_base::TXN_EMBRYO: return "TXN_EMBRYO";
+    case transaction_base::TXN_ACTIVE: return "TXN_ACTIVE";
+    case transaction_base::TXN_ABRT: return "TXN_ABRT";
+    case transaction_base::TXN_COMMITED: return "TXN_COMMITED";
+    }
+    ALWAYS_ASSERT(false);
+    return 0;
+  }
+
+  inline std::string
+  transaction_flags_to_str(uint64_t flags)
+  {
+    bool first = true;
+    std::ostringstream oss;
+    if (flags & transaction_base::TXN_FLAG_LOW_LEVEL_SCAN) {
+      oss << "TXN_FLAG_LOW_LEVEL_SCAN";
+      first = false;
+    }
+    if (flags & transaction_base::TXN_FLAG_READ_ONLY) {
+      if (first)
+        oss << "TXN_FLAG_READ_ONLY";
+      else
+        oss << " | TXN_FLAG_READ_ONLY";
+      first = false;
+    }
+    return oss.str();
+  }
+}
+
+template <template <typename> class Protocol, typename Traits>
+void
+transaction<Protocol, Traits>::dump_debug_info() const
+{
+  std::cerr << "Transaction (obj=" << util::hexify(this) << ") -- state "
+       << transaction_state_to_cstr(state) << std::endl;
+  std::cerr << "  Abort Reason: " << AbortReasonStr(reason) << std::endl;
+  std::cerr << "  Flags: " << transaction_flags_to_str(flags) << std::endl;
+  std::cerr << "  Read/Write sets:" << std::endl;
+
+  std::cerr << "      === Read Set ===" << std::endl;
+  // read-set
+  for (typename read_set_map::const_iterator rs_it = read_set.begin();
+       rs_it != read_set.end(); ++rs_it)
+    std::cerr << *rs_it << std::endl;
+
+  std::cerr << "      === Write Set ===" << std::endl;
+  // write-set
+  for (typename write_set_map::const_iterator ws_it = write_set.begin();
+       ws_it != write_set.end(); ++ws_it)
+    std::cerr << *ws_it << std::endl;
+
+  std::cerr << "      === Absent Set ===" << std::endl;
+  // absent-set
+  for (typename absent_set_map::const_iterator as_it = absent_set.begin();
+       as_it != absent_set.end(); ++as_it)
+    std::cerr << "      B-tree Node " << util::hexify(as_it->first)
+              << " : " << as_it->second << std::endl;
+
+}
+
+template <template <typename> class Protocol, typename Traits>
+std::map<std::string, uint64_t>
+transaction<Protocol, Traits>::get_txn_counters() const
+{
+  std::map<std::string, uint64_t> ret;
+
+  // max_read_set_size
+  ret["read_set_size"] = read_set.size();;
+  ret["read_set_is_large?"] = !read_set.is_small_type();
+
+  // max_absent_set_size
+  ret["absent_set_size"] = absent_set.size();
+  ret["absent_set_is_large?"] = !absent_set.is_small_type();
+
+  // max_write_set_size
+  ret["write_set_size"] = write_set.size();
+  ret["write_set_is_large?"] = !write_set.is_small_type();
+
+  return ret;
+}
+
+template <template <typename> class Protocol, typename Traits>
+bool
+transaction<Protocol, Traits>::handle_last_tuple_in_group(
+    dbtuple_write_info &last,
+    bool did_group_insert)
+{
+  if (did_group_insert) {
+    // don't need to lock
+    if (!last.is_insert())
+      // we inserted the last run, and then we did 1+ more overwrites
+      // to it, so we do NOT need to lock the node (again), but we DO
+      // need to apply the latest write
+      last.entry->set_do_write();
+  } else {
+    dbtuple *tuple = last.get_tuple();
+    if (unlikely(tuple->version == dbtuple::MAX_TID)) {
+      // if we race to put/insert w/ another txn which has inserted a new
+      // record, we *must* abort b/c the other txn could try to put/insert
+      // into a new record which we hold the lock on, so we must abort
+      //
+      // other ideas:
+      // we could *not* abort if this txn did not insert any new records.
+      // we could also release our insert locks and try to acquire them
+      // again in sorted order
+      return false; // signal abort
+    }
+    const dbtuple::version_t v = tuple->lock(true); // lock for write
+    INVARIANT(dbtuple::IsLatest(v) == tuple->is_latest());
+    last.mark_locked();
+    if (unlikely(!dbtuple::IsLatest(v) ||
+                 !cast()->can_read_tid(tuple->version))) {
+      // XXX(stephentu): overly conservative (with the can_read_tid() check)
+      return false; // signal abort
+    }
+    last.entry->set_do_write();
+  }
+  return true;
+}
+
+template <template <typename> class Protocol, typename Traits>
+bool
+transaction<Protocol, Traits>::commit(bool doThrow)
+{
+#ifdef TUPLE_MAGIC
+  try {
+#endif
+
+  PERF_DECL(
+      static std::string probe0_name(
+        std::string(__PRETTY_FUNCTION__) + std::string(":total:")));
+  ANON_REGION(probe0_name.c_str(), &transaction_base::g_txn_commit_probe0_cg);
+
+  switch (state) {
+  case TXN_EMBRYO:
+  case TXN_ACTIVE:
+    break;
+  case TXN_COMMITED:
+    return true;
+  case TXN_ABRT:
+    if (doThrow)
+      throw transaction_abort_exception(reason);
+    return false;
+  }
+
+  dbtuple_write_info_vec write_dbtuples;
+  std::pair<bool, tid_t> commit_tid(false, 0);
+
+  // copy write tuples to vector for sorting
+  if (!write_set.empty()) {
+    PERF_DECL(
+        static std::string probe1_name(
+          std::string(__PRETTY_FUNCTION__) + std::string(":lock_write_nodes:")));
+    ANON_REGION(probe1_name.c_str(), &transaction_base::g_txn_commit_probe1_cg);
+    INVARIANT(!is_snapshot());
+    typename write_set_map::iterator it     = write_set.begin();
+    typename write_set_map::iterator it_end = write_set.end();
+    for (size_t pos = 0; it != it_end; ++it, ++pos) {
+      INVARIANT(!it->is_insert() || it->get_tuple()->is_locked());
+      write_dbtuples.emplace_back(it->get_tuple(), &(*it), it->is_insert(), pos);
+    }
+  }
+
+  // read_only txns require consistent snapshots
+  INVARIANT(!is_snapshot() || read_set.empty());
+  INVARIANT(!is_snapshot() || write_set.empty());
+  INVARIANT(!is_snapshot() || absent_set.empty());
+  if (!is_snapshot()) {
+    // we don't have consistent tids, or not a read-only txn
+
+    // lock write nodes
+    if (!write_dbtuples.empty()) {
+      PERF_DECL(
+          static std::string probe2_name(
+            std::string(__PRETTY_FUNCTION__) + std::string(":lock_write_nodes:")));
+      ANON_REGION(probe2_name.c_str(), &transaction_base::g_txn_commit_probe2_cg);
+      // lock the logical nodes in sort order
+      {
+        PERF_DECL(
+            static std::string probe6_name(
+              std::string(__PRETTY_FUNCTION__) + std::string(":sort_write_nodes:")));
+        ANON_REGION(probe6_name.c_str(), &transaction_base::g_txn_commit_probe6_cg);
+        write_dbtuples.sort(); // in-place
+      }
+      typename dbtuple_write_info_vec::iterator it     = write_dbtuples.begin();
+      typename dbtuple_write_info_vec::iterator it_end = write_dbtuples.end();
+      dbtuple_write_info *last_px = nullptr;
+      bool inserted_last_run = false;
+      for (; it != it_end; last_px = &(*it), ++it) {
+        if (likely(last_px && last_px->tuple != it->tuple)) {
+          // on boundary
+          if (unlikely(!handle_last_tuple_in_group(*last_px, inserted_last_run))) {
+            abort_trap((reason = ABORT_REASON_WRITE_NODE_INTERFERENCE));
+            goto do_abort;
+          }
+          inserted_last_run = false;
+        }
+        if (it->is_insert()) {
+          INVARIANT(!last_px || last_px->tuple != it->tuple);
+          INVARIANT(it->is_locked());
+          INVARIANT(it->get_tuple()->is_locked());
+          INVARIANT(it->get_tuple()->is_lock_owner());
+          it->entry->set_do_write(); // all inserts are marked do-write
+          inserted_last_run = true;
+        } else {
+          INVARIANT(!it->is_locked());
+        }
+      }
+      if (likely(last_px) &&
+          unlikely(!handle_last_tuple_in_group(*last_px, inserted_last_run))) {
+        abort_trap((reason = ABORT_REASON_WRITE_NODE_INTERFERENCE));
+        goto do_abort;
+      }
+      commit_tid.first = true;
+      PERF_DECL(
+          static std::string probe5_name(
+            std::string(__PRETTY_FUNCTION__) + std::string(":gen_commit_tid:")));
+      ANON_REGION(probe5_name.c_str(), &transaction_base::g_txn_commit_probe5_cg);
+      commit_tid.second = cast()->gen_commit_tid(write_dbtuples);
+      VERBOSE(std::cerr << "commit tid: " << g_proto_version_str(commit_tid.second) << std::endl);
+    } else {
+      VERBOSE(std::cerr << "commit tid: <read-only>" << std::endl);
+    }
+
+    // do read validation
+    {
+      PERF_DECL(
+          static std::string probe3_name(
+            std::string(__PRETTY_FUNCTION__) + std::string(":read_validation:")));
+      ANON_REGION(probe3_name.c_str(), &transaction_base::g_txn_commit_probe3_cg);
+
+      // check the nodes we actually read are still the latest version
+      if (!read_set.empty()) {
+        typename read_set_map::iterator it     = read_set.begin();
+        typename read_set_map::iterator it_end = read_set.end();
+        for (; it != it_end; ++it) {
+          VERBOSE(std::cerr << "validating dbtuple " << util::hexify(it->get_tuple())
+                            << " at snapshot_tid "
+                            << g_proto_version_str(cast()->snapshot_tid())
+                            << std::endl);
+          const bool found = sorted_dbtuples_contains(
+              write_dbtuples, it->get_tuple());
+          if (likely(found ?
+                it->get_tuple()->is_latest_version(it->get_tid()) :
+                it->get_tuple()->stable_is_latest_version(it->get_tid())))
+            continue;
+
+          VERBOSE(std::cerr << "validating dbtuple " << util::hexify(it->get_tuple()) << " at snapshot_tid "
+                            << g_proto_version_str(cast()->snapshot_tid()) << " FAILED" << std::endl
+                            << "  txn read version: " << g_proto_version_str(it->get_tid()) << std::endl
+                            << "  tuple=" << *it->get_tuple() << std::endl);
+
+          //std::cerr << "failed tuple: " << *it->get_tuple() << std::endl;
+
+          abort_trap((reason = ABORT_REASON_READ_NODE_INTEREFERENCE));
+          goto do_abort;
+        }
+      }
+
+      // check btree versions have not changed
+      if (!absent_set.empty()) {
+        typename absent_set_map::iterator it     = absent_set.begin();
+        typename absent_set_map::iterator it_end = absent_set.end();
+        for (; it != it_end; ++it) {
+          const uint64_t v = concurrent_btree::ExtractVersionNumber(it->first);
+          if (unlikely(v != it->second.version)) {
+            VERBOSE(std::cerr << "expected node " << util::hexify(it->first) << " at v="
+                              << it->second.version << ", got v=" << v << std::endl);
+            abort_trap((reason = ABORT_REASON_NODE_SCAN_READ_VERSION_CHANGED));
+            goto do_abort;
+          }
+        }
+      }
+    }
+
+    // commit actual records
+    if (!write_dbtuples.empty()) {
+      PERF_DECL(
+          static std::string probe4_name(
+            std::string(__PRETTY_FUNCTION__) + std::string(":write_records:")));
+      ANON_REGION(probe4_name.c_str(), &transaction_base::g_txn_commit_probe4_cg);
+      typename write_set_map::iterator it     = write_set.begin();
+      typename write_set_map::iterator it_end = write_set.end();
+      for (; it != it_end; ++it) {
+        if (unlikely(!it->do_write()))
+          continue;
+        dbtuple * const tuple = it->get_tuple();
+        INVARIANT(tuple->is_locked());
+        VERBOSE(std::cerr << "writing dbtuple " << util::hexify(tuple)
+                          << " at commit_tid " << g_proto_version_str(commit_tid.second)
+                          << std::endl);
+        if (it->is_insert()) {
+          INVARIANT(tuple->version == dbtuple::MAX_TID);
+          tuple->version = commit_tid.second; // allows write_record_ret() to succeed
+                                              // w/o creating a new chain
+        } else {
+          tuple->prefetch();
+          const dbtuple::write_record_ret ret =
+            tuple->write_record_at(
+                cast(), commit_tid.second,
+                it->get_value(), it->get_writer());
+          bool unlock_head = false;
+          if (unlikely(ret.head_ != tuple)) {
+            // tuple was replaced by ret.head_
+            INVARIANT(ret.rest_ == tuple);
+            // XXX: write_record_at() should acquire this lock
+            ret.head_->lock(true);
+            unlock_head = true;
+            // need to unlink tuple from underlying btree, replacing
+            // with ret.rest_ (atomically)
+            typename concurrent_btree::value_type old_v = 0;
+            if (it->get_btree()->insert(
+                  varkey(it->get_key()), (typename concurrent_btree::value_type) ret.head_, &old_v, NULL))
+              // should already exist in tree
+              INVARIANT(false);
+            INVARIANT(old_v == (typename concurrent_btree::value_type) tuple);
+            // we don't RCU free this, because it is now part of the chain
+            // (the cleaners will take care of this)
+            ++evt_dbtuple_latest_replacement;
+          }
+          if (unlikely(ret.rest_))
+            // spill happened: schedule GC task
+            cast()->on_dbtuple_spill(ret.head_, ret.rest_);
+          if (!it->get_value())
+            // logical delete happened: schedule GC task
+            cast()->on_logical_delete(ret.head_, it->get_key(), it->get_btree());
+          if (unlikely(unlock_head))
+            ret.head_->unlock();
+        }
+        VERBOSE(std::cerr << "dbtuple " << util::hexify(tuple) << " is_locked? " << tuple->is_locked() << std::endl);
+      }
+      // unlock
+      // NB: we can no longer un-lock after doing the writes above
+      for (typename dbtuple_write_info_vec::iterator it = write_dbtuples.begin();
+           it != write_dbtuples.end(); ++it) {
+        if (it->is_locked())
+          it->tuple->unlock();
+        else
+          INVARIANT(!it->is_insert());
+      }
+    }
+  }
+  state = TXN_COMMITED;
+  if (commit_tid.first)
+    cast()->on_tid_finish(commit_tid.second);
+  clear();
+  return true;
+
+do_abort:
+  // XXX: these values are possibly un-initialized
+  if (this->is_snapshot())
+    VERBOSE(std::cerr << "aborting txn @ snapshot_tid " << cast()->snapshot_tid() << std::endl);
+  else
+    VERBOSE(std::cerr << "aborting txn" << std::endl);
+
+  for (typename dbtuple_write_info_vec::iterator it = write_dbtuples.begin();
+       it != write_dbtuples.end(); ++it) {
+    if (it->is_locked()) {
+      if (it->is_insert()) {
+        INVARIANT(it->entry);
+        this->cleanup_inserted_tuple_marker(
+            it->tuple.get(), it->entry->get_key(), it->entry->get_btree());
+      }
+      // XXX: potential optimization: on unlock() for abort, we don't
+      // technically need to change the version number
+      it->tuple->unlock();
+    } else {
+      INVARIANT(!it->is_insert());
+    }
+  }
+
+  state = TXN_ABRT;
+  if (commit_tid.first)
+    cast()->on_tid_finish(commit_tid.second);
+  clear();
+  if (doThrow)
+    throw transaction_abort_exception(reason);
+  return false;
+
+#ifdef TUPLE_MAGIC
+  } catch (dbtuple::magic_failed_exception &) {
+    dump_debug_info();
+    ALWAYS_ASSERT(false);
+  }
+#endif
+}
+
+template <template <typename> class Protocol, typename Traits>
+std::pair< dbtuple *, bool >
+transaction<Protocol, Traits>::try_insert_new_tuple(
+    concurrent_btree &btr,
+    const std::string *key,
+    const void *value,
+    dbtuple::tuple_writer_t writer)
+{
+  INVARIANT(key);
+  const size_t sz =
+    value ? writer(dbtuple::TUPLE_WRITER_COMPUTE_NEEDED,
+      value, nullptr, 0) : 0;
+
+  // perf: ~900 tsc/alloc on istc11.csail.mit.edu
+  dbtuple * const tuple = dbtuple::alloc_first(sz, true);
+  if (value)
+    writer(dbtuple::TUPLE_WRITER_DO_WRITE,
+        value, tuple->get_value_start(), 0);
+  INVARIANT(find_read_set(tuple) == read_set.end());
+  INVARIANT(tuple->is_latest());
+  INVARIANT(tuple->version == dbtuple::MAX_TID);
+  INVARIANT(tuple->is_locked());
+  INVARIANT(tuple->is_write_intent());
+#ifdef TUPLE_CHECK_KEY
+  tuple->key.assign(key->data(), key->size());
+  tuple->tree = (void *) &btr;
+#endif
+
+  // XXX: underlying btree api should return the existing value if insert
+  // fails- this would allow us to avoid having to do another search
+  typename concurrent_btree::insert_info_t insert_info;
+  if (unlikely(!btr.insert_if_absent(
+          varkey(*key), (typename concurrent_btree::value_type) tuple, &insert_info))) {
+    VERBOSE(std::cerr << "insert_if_absent failed for key: " << util::hexify(key) << std::endl);
+    tuple->clear_latest();
+    tuple->unlock();
+    dbtuple::release_no_rcu(tuple);
+    ++transaction_base::g_evt_dbtuple_write_insert_failed;
+    return std::pair< dbtuple *, bool >(nullptr, false);
+  }
+  VERBOSE(std::cerr << "insert_if_absent suceeded for key: " << util::hexify(key) << std::endl
+                    << "  new dbtuple is " << util::hexify(tuple) << std::endl);
+  // update write_set
+  // too expensive to be practical
+  // INVARIANT(find_write_set(tuple) == write_set.end());
+  write_set.emplace_back(tuple, key, value, writer, &btr, true);
+
+  // update node #s
+  INVARIANT(insert_info.node);
+  if (!absent_set.empty()) {
+    auto it = absent_set.find(insert_info.node);
+    if (it != absent_set.end()) {
+      if (unlikely(it->second.version != insert_info.old_version)) {
+        abort_trap((reason = ABORT_REASON_WRITE_NODE_INTERFERENCE));
+        return std::make_pair(tuple, true);
+      }
+      VERBOSE(std::cerr << "bump node=" << util::hexify(it->first) << " from v=" << insert_info.old_version
+                        << " -> v=" << insert_info.new_version << std::endl);
+      // otherwise, bump the version
+      it->second.version = insert_info.new_version;
+      SINGLE_THREADED_INVARIANT(concurrent_btree::ExtractVersionNumber(it->first) == it->second);
+    }
+  }
+  return std::make_pair(tuple, false);
+}
+
+template <template <typename> class Protocol, typename Traits>
+template <typename ValueReader>
+bool
+transaction<Protocol, Traits>::do_tuple_read(
+    const dbtuple *tuple, ValueReader &value_reader)
+{
+  INVARIANT(tuple);
+  ++evt_local_search_lookups;
+
+  const bool is_snapshot_txn = is_snapshot();
+  const transaction_base::tid_t snapshot_tid = is_snapshot_txn ?
+    cast()->snapshot_tid() : static_cast<transaction_base::tid_t>(dbtuple::MAX_TID);
+  transaction_base::tid_t start_t = 0;
+
+  if (Traits::read_own_writes) {
+    // this is why read_own_writes is not performant, because we have
+    // to do linear scan
+    auto write_set_it = find_write_set(const_cast<dbtuple *>(tuple));
+    if (unlikely(write_set_it != write_set.end())) {
+      ++evt_local_search_write_set_hits;
+      if (!write_set_it->get_value())
+        return false;
+      const typename ValueReader::value_type * const px =
+        reinterpret_cast<const typename ValueReader::value_type *>(
+            write_set_it->get_value());
+      value_reader.dup(*px, this->string_allocator());
+      return true;
+    }
+  }
+
+  // do the actual tuple read
+  dbtuple::ReadStatus stat;
+  {
+    PERF_DECL(static std::string probe0_name(std::string(__PRETTY_FUNCTION__) + std::string(":do_read:")));
+    ANON_REGION(probe0_name.c_str(), &private_::txn_btree_search_probe0_cg);
+    tuple->prefetch();
+    stat = tuple->stable_read(snapshot_tid, start_t, value_reader, this->string_allocator(), is_snapshot_txn);
+    if (unlikely(stat == dbtuple::READ_FAILED)) {
+      const transaction_base::abort_reason r = transaction_base::ABORT_REASON_UNSTABLE_READ;
+      abort_impl(r);
+      throw transaction_abort_exception(r);
+    }
+  }
+  if (unlikely(!cast()->can_read_tid(start_t))) {
+    const transaction_base::abort_reason r = transaction_base::ABORT_REASON_FUTURE_TID_READ;
+    abort_impl(r);
+    throw transaction_abort_exception(r);
+  }
+  INVARIANT(stat == dbtuple::READ_EMPTY ||
+            stat == dbtuple::READ_RECORD);
+  const bool v_empty = (stat == dbtuple::READ_EMPTY);
+  if (v_empty)
+    ++transaction_base::g_evt_read_logical_deleted_node_search;
+  if (!is_snapshot_txn)
+    // read-only txns do not need read-set tracking
+    // (b/c we know the values are consistent)
+    read_set.emplace_back(tuple, start_t);
+  return !v_empty;
+}
+
+template <template <typename> class Protocol, typename Traits>
+void
+transaction<Protocol, Traits>::do_node_read(
+    const typename concurrent_btree::node_opaque_t *n, uint64_t v)
+{
+  INVARIANT(n);
+  if (is_snapshot())
+    return;
+  auto it = absent_set.find(n);
+  if (it == absent_set.end()) {
+    absent_set[n].version = v;
+  } else if (it->second.version != v) {
+    const transaction_base::abort_reason r =
+      transaction_base::ABORT_REASON_NODE_SCAN_READ_VERSION_CHANGED;
+    abort_impl(r);
+    throw transaction_abort_exception(r);
+  }
+}
+
+#endif /* _NDB_TXN_IMPL_H_ */
diff --git a/silo/txn_proto2_impl.cc b/silo/txn_proto2_impl.cc
new file mode 100644 (file)
index 0000000..fad91be
--- /dev/null
@@ -0,0 +1,716 @@
+#include <iostream>
+#include <thread>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <limits.h>
+#include <numa.h>
+
+#include "txn_proto2_impl.h"
+#include "counter.h"
+#include "util.h"
+
+using namespace std;
+using namespace util;
+
+                    /** logger subsystem **/
+/*{{{*/
+bool txn_logger::g_persist = false;
+bool txn_logger::g_call_fsync = true;
+bool txn_logger::g_use_compression = false;
+bool txn_logger::g_fake_writes = false;
+size_t txn_logger::g_nworkers = 0;
+txn_logger::epoch_array
+  txn_logger::per_thread_sync_epochs_[txn_logger::g_nmax_loggers];
+aligned_padded_elem<atomic<uint64_t>>
+  txn_logger::system_sync_epoch_(0);
+percore<txn_logger::persist_ctx>
+  txn_logger::g_persist_ctxs;
+percore<txn_logger::persist_stats>
+  txn_logger::g_persist_stats;
+event_counter
+  txn_logger::g_evt_log_buffer_epoch_boundary("log_buffer_epoch_boundary");
+event_counter
+  txn_logger::g_evt_log_buffer_out_of_space("log_buffer_out_of_space");
+event_counter
+  txn_logger::g_evt_log_buffer_bytes_before_compress("log_buffer_bytes_before_compress");
+event_counter
+  txn_logger::g_evt_log_buffer_bytes_after_compress("log_buffer_bytes_after_compress");
+event_counter
+  txn_logger::g_evt_logger_writev_limit_met("logger_writev_limit_met");
+event_counter
+  txn_logger::g_evt_logger_max_lag_wait("logger_max_lag_wait");
+event_avg_counter
+  txn_logger::g_evt_avg_log_buffer_compress_time_us("avg_log_buffer_compress_time_us");
+event_avg_counter
+  txn_logger::g_evt_avg_log_entry_ntxns("avg_log_entry_ntxns_per_entry");
+event_avg_counter
+  txn_logger::g_evt_avg_logger_bytes_per_writev("avg_logger_bytes_per_writev");
+event_avg_counter
+  txn_logger::g_evt_avg_logger_bytes_per_sec("avg_logger_bytes_per_sec");
+
+static event_avg_counter
+  evt_avg_log_buffer_iov_len("avg_log_buffer_iov_len");
+
+void
+txn_logger::Init(
+    size_t nworkers,
+    const vector<string> &logfiles,
+    const vector<vector<unsigned>> &assignments_given,
+    vector<vector<unsigned>> *assignments_used,
+    bool call_fsync,
+    bool use_compression,
+    bool fake_writes)
+{
+  INVARIANT(!g_persist);
+  INVARIANT(g_nworkers == 0);
+  INVARIANT(nworkers > 0);
+  INVARIANT(!logfiles.empty());
+  INVARIANT(logfiles.size() <= g_nmax_loggers);
+  INVARIANT(!use_compression || g_perthread_buffers > 1); // need 1 as scratch buf
+  vector<int> fds;
+  for (auto &fname : logfiles) {
+    int fd = open(fname.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0664);
+    if (fd == -1) {
+      perror("open");
+      ALWAYS_ASSERT(false);
+    }
+    fds.push_back(fd);
+  }
+  g_persist = true;
+  g_call_fsync = call_fsync;
+  g_use_compression = use_compression;
+  g_fake_writes = fake_writes;
+  g_nworkers = nworkers;
+
+  for (size_t i = 0; i < g_nmax_loggers; i++)
+    for (size_t j = 0; j < g_nworkers; j++)
+      per_thread_sync_epochs_[i].epochs_[j].store(0, memory_order_release);
+
+  vector<thread> writers;
+  vector<vector<unsigned>> assignments(assignments_given);
+
+  if (assignments.empty()) {
+    // compute assuming homogenous disks
+    if (g_nworkers <= fds.size()) {
+      // each thread gets its own logging worker
+      for (size_t i = 0; i < g_nworkers; i++)
+        assignments.push_back({(unsigned) i});
+    } else {
+      // XXX: currently we assume each logger is equally as fast- we should
+      // adjust ratios accordingly for non-homogenous loggers
+      const size_t threads_per_logger = g_nworkers / fds.size();
+      for (size_t i = 0; i < fds.size(); i++) {
+        assignments.emplace_back(
+            MakeRange<unsigned>(
+              i * threads_per_logger,
+              ((i + 1) == fds.size()) ?  g_nworkers : (i + 1) * threads_per_logger));
+      }
+    }
+  }
+
+  INVARIANT(AssignmentsValid(assignments, fds.size(), g_nworkers));
+
+  for (size_t i = 0; i < assignments.size(); i++) {
+    writers.emplace_back(
+        &txn_logger::writer,
+        i, fds[i], assignments[i]);
+    writers.back().detach();
+  }
+
+  thread persist_thread(&txn_logger::persister, assignments);
+  persist_thread.detach();
+
+  if (assignments_used)
+    *assignments_used = assignments;
+}
+
+void
+txn_logger::persister(
+    vector<vector<unsigned>> assignments)
+{
+  timer loop_timer;
+  for (;;) {
+    const uint64_t last_loop_usec = loop_timer.lap();
+    const uint64_t delay_time_usec = ticker::tick_us;
+    if (last_loop_usec < delay_time_usec) {
+      const uint64_t sleep_ns = (delay_time_usec - last_loop_usec) * 1000;
+      struct timespec t;
+      t.tv_sec  = sleep_ns / ONE_SECOND_NS;
+      t.tv_nsec = sleep_ns % ONE_SECOND_NS;
+      nanosleep(&t, nullptr);
+    }
+    advance_system_sync_epoch(assignments);
+  }
+}
+
+void
+txn_logger::advance_system_sync_epoch(
+    const vector<vector<unsigned>> &assignments)
+{
+  uint64_t min_so_far = numeric_limits<uint64_t>::max();
+  const uint64_t best_tick_ex =
+    ticker::s_instance.global_current_tick();
+  // special case 0
+  const uint64_t best_tick_inc =
+    best_tick_ex ? (best_tick_ex - 1) : 0;
+
+  for (size_t i = 0; i < assignments.size(); i++)
+    for (auto j : assignments[i])
+      for (size_t k = j; k < NMAXCORES; k += g_nworkers) {
+        persist_ctx &ctx = persist_ctx_for(k, INITMODE_NONE);
+        // we need to arbitrarily advance threads which are not "doing
+        // anything", so they don't drag down the persistence of the system. if
+        // we can see that a thread is NOT in a guarded section AND its
+        // core->logger queue is empty, then that means we can advance its sync
+        // epoch up to best_tick_inc, b/c it is guaranteed that the next time
+        // it does any actions will be in epoch > best_tick_inc
+        if (!ctx.persist_buffers_.peek()) {
+          spinlock &l = ticker::s_instance.lock_for(k);
+          if (!l.is_locked()) {
+            bool did_lock = false;
+            for (size_t c = 0; c < 3; c++) {
+              if (l.try_lock()) {
+                did_lock = true;
+                break;
+              }
+            }
+            if (did_lock) {
+              if (!ctx.persist_buffers_.peek()) {
+                min_so_far = min(min_so_far, best_tick_inc);
+                per_thread_sync_epochs_[i].epochs_[k].store(
+                    best_tick_inc, memory_order_release);
+                l.unlock();
+                continue;
+              }
+              l.unlock();
+            }
+          }
+        }
+        min_so_far = min(
+            per_thread_sync_epochs_[i].epochs_[k].load(
+              memory_order_acquire),
+            min_so_far);
+      }
+
+  const uint64_t syssync =
+    system_sync_epoch_->load(memory_order_acquire);
+
+  INVARIANT(min_so_far < numeric_limits<uint64_t>::max());
+  INVARIANT(syssync <= min_so_far);
+
+  // need to aggregate from [syssync + 1, min_so_far]
+  const uint64_t now_us = timer::cur_usec();
+  for (size_t i = 0; i < g_persist_stats.size(); i++) {
+    auto &ps = g_persist_stats[i];
+    for (uint64_t e = syssync + 1; e <= min_so_far; e++) {
+        auto &pes = ps.d_[e % g_max_lag_epochs];
+        const uint64_t ntxns_in_epoch = pes.ntxns_.load(memory_order_acquire);
+        const uint64_t start_us = pes.earliest_start_us_.load(memory_order_acquire);
+        INVARIANT(now_us >= start_us);
+        non_atomic_fetch_add(ps.ntxns_persisted_, ntxns_in_epoch);
+        non_atomic_fetch_add(
+            ps.latency_numer_,
+            (now_us - start_us) * ntxns_in_epoch);
+        pes.ntxns_.store(0, memory_order_release);
+        pes.earliest_start_us_.store(0, memory_order_release);
+    }
+  }
+
+  system_sync_epoch_->store(min_so_far, memory_order_release);
+}
+
+void
+txn_logger::writer(
+    unsigned id, int fd,
+    vector<unsigned> assignment)
+{
+
+  if (g_pin_loggers_to_numa_nodes) {
+    ALWAYS_ASSERT(!numa_run_on_node(id % numa_num_configured_nodes()));
+    ALWAYS_ASSERT(!sched_yield());
+  }
+
+  vector<iovec> iovs(
+      min(size_t(IOV_MAX), g_nworkers * g_perthread_buffers));
+  vector<pbuffer *> pxs;
+  timer loop_timer;
+
+  // XXX: sense is not useful for now, unless we want to
+  // fsync in the background...
+  bool sense = false; // cur is at sense, prev is at !sense
+  uint64_t epoch_prefixes[2][NMAXCORES];
+
+  NDB_MEMSET(&epoch_prefixes[0], 0, sizeof(epoch_prefixes[0]));
+  NDB_MEMSET(&epoch_prefixes[1], 0, sizeof(epoch_prefixes[1]));
+
+  // NOTE: a core id in the persistence system really represets
+  // all cores in the regular system modulo g_nworkers
+  size_t nbufswritten = 0, nbyteswritten = 0;
+  for (;;) {
+
+    const uint64_t last_loop_usec = loop_timer.lap();
+    const uint64_t delay_time_usec = ticker::tick_us;
+    // don't allow this loop to proceed less than an epoch's worth of time,
+    // so we can batch IO
+    if (last_loop_usec < delay_time_usec && nbufswritten < iovs.size()) {
+      const uint64_t sleep_ns = (delay_time_usec - last_loop_usec) * 1000;
+      struct timespec t;
+      t.tv_sec  = sleep_ns / ONE_SECOND_NS;
+      t.tv_nsec = sleep_ns % ONE_SECOND_NS;
+      nanosleep(&t, nullptr);
+    }
+
+    // we need g_persist_stats[cur_sync_epoch_ex % g_nmax_loggers]
+    // to remain untouched (until the syncer can catch up), so we
+    // cannot read any buffers with epoch >=
+    // (cur_sync_epoch_ex + g_max_lag_epochs)
+    const uint64_t cur_sync_epoch_ex =
+      system_sync_epoch_->load(memory_order_acquire) + 1;
+    nbufswritten = nbyteswritten = 0;
+    for (auto idx : assignment) {
+      INVARIANT(idx >= 0 && idx < g_nworkers);
+      for (size_t k = idx; k < NMAXCORES; k += g_nworkers) {
+        persist_ctx &ctx = persist_ctx_for(k, INITMODE_NONE);
+        ctx.persist_buffers_.peekall(pxs);
+        for (auto px : pxs) {
+          INVARIANT(px);
+          INVARIANT(!px->io_scheduled_);
+          INVARIANT(nbufswritten <= iovs.size());
+          INVARIANT(px->header()->nentries_);
+          INVARIANT(px->core_id_ == k);
+          if (nbufswritten == iovs.size()) {
+            ++g_evt_logger_writev_limit_met;
+            goto process;
+          }
+          if (transaction_proto2_static::EpochId(px->header()->last_tid_) >=
+              cur_sync_epoch_ex + g_max_lag_epochs) {
+            ++g_evt_logger_max_lag_wait;
+            break;
+          }
+          iovs[nbufswritten].iov_base = (void *) &px->buf_start_[0];
+
+#ifdef LOGGER_UNSAFE_REDUCE_BUFFER_SIZE
+  #define PXLEN(px) (((px)->curoff_ < 4) ? (px)->curoff_ : ((px)->curoff_ / 4))
+#else
+  #define PXLEN(px) ((px)->curoff_)
+#endif
+
+          const size_t pxlen = PXLEN(px);
+
+          iovs[nbufswritten].iov_len = pxlen;
+          evt_avg_log_buffer_iov_len.offer(pxlen);
+          px->io_scheduled_ = true;
+          nbufswritten++;
+          nbyteswritten += pxlen;
+
+#ifdef CHECK_INVARIANTS
+          auto last_tid_cid = transaction_proto2_static::CoreId(px->header()->last_tid_);
+          auto px_cid = px->core_id_;
+          if (last_tid_cid != px_cid) {
+            cerr << "header: " << *px->header() << endl;
+            cerr << g_proto_version_str(last_tid_cid) << endl;
+            cerr << "last_tid_cid: " << last_tid_cid << endl;
+            cerr << "px_cid: " << px_cid << endl;
+          }
+#endif
+
+          const uint64_t px_epoch =
+            transaction_proto2_static::EpochId(px->header()->last_tid_);
+          INVARIANT(
+              transaction_proto2_static::CoreId(px->header()->last_tid_) ==
+              px->core_id_);
+          INVARIANT(epoch_prefixes[sense][k] <= px_epoch);
+          INVARIANT(px_epoch > 0);
+          epoch_prefixes[sense][k] = px_epoch - 1;
+          auto &pes = g_persist_stats[k].d_[px_epoch % g_max_lag_epochs];
+          if (!pes.ntxns_.load(memory_order_acquire))
+            pes.earliest_start_us_.store(px->earliest_start_us_, memory_order_release);
+          non_atomic_fetch_add(pes.ntxns_, px->header()->nentries_);
+          g_evt_avg_log_entry_ntxns.offer(px->header()->nentries_);
+        }
+      }
+    }
+
+  process:
+    if (!nbufswritten) {
+      // XXX: should probably sleep here
+      nop_pause();
+      continue;
+    }
+
+    const bool dosense = sense;
+
+    if (!g_fake_writes) {
+#ifdef ENABLE_EVENT_COUNTERS
+      timer write_timer;
+#endif
+      const ssize_t ret = writev(fd, &iovs[0], nbufswritten);
+      if (unlikely(ret == -1)) {
+        perror("writev");
+        ALWAYS_ASSERT(false);
+      }
+
+      if (g_call_fsync) {
+        const int fret = fdatasync(fd);
+        if (unlikely(fret == -1)) {
+          perror("fdatasync");
+          ALWAYS_ASSERT(false);
+        }
+      }
+
+#ifdef ENABLE_EVENT_COUNTERS
+      {
+        g_evt_avg_logger_bytes_per_writev.offer(nbyteswritten);
+        const double bytes_per_sec =
+          double(nbyteswritten)/(write_timer.lap_ms() / 1000.0);
+        g_evt_avg_logger_bytes_per_sec.offer(bytes_per_sec);
+      }
+#endif
+    }
+
+    // update metadata from previous write
+    //
+    // return all buffers that have been io_scheduled_ - we can do this as
+    // soon as write returns. we take care to return to the proper buffer
+    epoch_array &ea = per_thread_sync_epochs_[id];
+    for (auto idx: assignment) {
+      for (size_t k = idx; k < NMAXCORES; k += g_nworkers) {
+        const uint64_t x0 = ea.epochs_[k].load(memory_order_acquire);
+        const uint64_t x1 = epoch_prefixes[dosense][k];
+        if (x1 > x0)
+          ea.epochs_[k].store(x1, memory_order_release);
+
+        persist_ctx &ctx = persist_ctx_for(k, INITMODE_NONE);
+        pbuffer *px, *px0;
+        while ((px = ctx.persist_buffers_.peek()) && px->io_scheduled_) {
+#ifdef LOGGER_STRIDE_OVER_BUFFER
+          {
+            const size_t pxlen = PXLEN(px);
+            const size_t stridelen = 1;
+            for (size_t p = 0; p < pxlen; p += stridelen)
+              if ((&px->buf_start_[0])[p] & 0xF)
+                non_atomic_fetch_add(ea.dummy_work_, 1UL);
+          }
+#endif
+          px0 = ctx.persist_buffers_.deq();
+          INVARIANT(px == px0);
+          INVARIANT(px->header()->nentries_);
+          px0->reset();
+          INVARIANT(ctx.init_);
+          INVARIANT(px0->core_id_ == k);
+          ctx.all_buffers_.enq(px0);
+        }
+      }
+    }
+
+    // bump the sense
+    sense = !sense;
+  }
+}
+
+tuple<uint64_t, uint64_t, double>
+txn_logger::compute_ntxns_persisted_statistics()
+{
+  uint64_t acc = 0, acc1 = 0, acc2 = 0;
+  uint64_t num = 0;
+  for (size_t i = 0; i < g_persist_stats.size(); i++) {
+    acc  += g_persist_stats[i].ntxns_persisted_.load(memory_order_acquire);
+    acc1 += g_persist_stats[i].ntxns_pushed_.load(memory_order_acquire);
+    acc2 += g_persist_stats[i].ntxns_committed_.load(memory_order_acquire);
+    num  += g_persist_stats[i].latency_numer_.load(memory_order_acquire);
+  }
+  INVARIANT(acc <= acc1);
+  INVARIANT(acc1 <= acc2);
+  if (acc == 0)
+    return make_tuple(0, acc1, 0.0);
+  return make_tuple(acc, acc1, double(num)/double(acc));
+}
+
+void
+txn_logger::clear_ntxns_persisted_statistics()
+{
+  for (size_t i = 0; i < g_persist_stats.size(); i++) {
+    auto &ps = g_persist_stats[i];
+    ps.ntxns_persisted_.store(0, memory_order_release);
+    ps.ntxns_pushed_.store(0, memory_order_release);
+    ps.ntxns_committed_.store(0, memory_order_release);
+    ps.latency_numer_.store(0, memory_order_release);
+    for (size_t e = 0; e < g_max_lag_epochs; e++) {
+      auto &pes = ps.d_[e];
+      pes.ntxns_.store(0, memory_order_release);
+      pes.earliest_start_us_.store(0, memory_order_release);
+    }
+  }
+}
+
+void
+txn_logger::wait_for_idle_state()
+{
+  for (size_t i = 0; i < NMAXCORES; i++) {
+    persist_ctx &ctx = persist_ctx_for(i, INITMODE_NONE);
+    if (!ctx.init_)
+      continue;
+    pbuffer *px;
+    while (!(px = ctx.all_buffers_.peek()) || px->header()->nentries_)
+      nop_pause();
+    while (ctx.persist_buffers_.peek())
+      nop_pause();
+  }
+}
+
+void
+txn_logger::wait_until_current_point_persisted()
+{
+  const uint64_t e = ticker::s_instance.global_current_tick();
+  cerr << "waiting for system_sync_epoch_="
+       << system_sync_epoch_->load(memory_order_acquire)
+       << " to be < e=" << e << endl;
+  while (system_sync_epoch_->load(memory_order_acquire) < e)
+    nop_pause();
+}
+/*}}}*/
+
+                /** garbage collection subsystem **/
+
+static event_counter evt_local_chain_cleanups("local_chain_cleanups");
+static event_counter evt_try_delete_unlinks("try_delete_unlinks");
+static event_avg_counter evt_avg_time_inbetween_ro_epochs_usec(
+    "avg_time_inbetween_ro_epochs_usec");
+
+void
+transaction_proto2_static::InitGC()
+{
+  g_flags->g_gc_init.store(true, memory_order_release);
+}
+
+static void
+sleep_ro_epoch()
+{
+  const uint64_t sleep_ns = transaction_proto2_static::ReadOnlyEpochUsec * 1000;
+  struct timespec t;
+  t.tv_sec  = sleep_ns / ONE_SECOND_NS;
+  t.tv_nsec = sleep_ns % ONE_SECOND_NS;
+  nanosleep(&t, nullptr);
+}
+
+void
+transaction_proto2_static::PurgeThreadOutstandingGCTasks()
+{
+#ifdef PROTO2_CAN_DISABLE_GC
+  if (!IsGCEnabled())
+    return;
+#endif
+  INVARIANT(!rcu::s_instance.in_rcu_region());
+  threadctx &ctx = g_threadctxs.my();
+  uint64_t e;
+  if (!ctx.queue_.get_latest_epoch(e))
+    return;
+  // wait until we can clean up e
+  for (;;) {
+    const uint64_t last_tick_ex = ticker::s_instance.global_last_tick_exclusive();
+    const uint64_t ro_tick_ex = to_read_only_tick(last_tick_ex);
+    if (unlikely(!ro_tick_ex)) {
+      sleep_ro_epoch();
+      continue;
+    }
+    const uint64_t ro_tick_geq = ro_tick_ex - 1;
+    if (ro_tick_geq < e) {
+      sleep_ro_epoch();
+      continue;
+    }
+    break;
+  }
+  clean_up_to_including(ctx, e);
+  INVARIANT(ctx.queue_.empty());
+}
+
+//#ifdef CHECK_INVARIANTS
+//// make sure hidden is blocked by version e, when traversing from start
+//static bool
+//IsBlocked(dbtuple *start, dbtuple *hidden, uint64_t e)
+//{
+//  dbtuple *c = start;
+//  while (c) {
+//    if (c == hidden)
+//      return false;
+//    if (c->is_not_behind(e))
+//      // blocked
+//      return true;
+//    c = c->next;
+//  }
+//  ALWAYS_ASSERT(false); // hidden should be found on chain
+//}
+//#endif
+
+void
+transaction_proto2_static::clean_up_to_including(threadctx &ctx, uint64_t ro_tick_geq)
+{
+  INVARIANT(!rcu::s_instance.in_rcu_region());
+  INVARIANT(ctx.last_reaped_epoch_ <= ro_tick_geq);
+  INVARIANT(ctx.scratch_.empty());
+  if (ctx.last_reaped_epoch_ == ro_tick_geq)
+    return;
+
+#ifdef ENABLE_EVENT_COUNTERS
+  const uint64_t now = timer::cur_usec();
+  if (ctx.last_reaped_timestamp_us_ > 0) {
+    const uint64_t diff = now - ctx.last_reaped_timestamp_us_;
+    evt_avg_time_inbetween_ro_epochs_usec.offer(diff);
+  }
+  ctx.last_reaped_timestamp_us_ = now;
+#endif
+  ctx.last_reaped_epoch_ = ro_tick_geq;
+
+#ifdef CHECK_INVARIANTS
+  const uint64_t last_tick_ex = ticker::s_instance.global_last_tick_exclusive();
+  INVARIANT(last_tick_ex);
+  const uint64_t last_consistent_tid = ComputeReadOnlyTid(last_tick_ex - 1);
+  const uint64_t computed_last_tick_ex = ticker::s_instance.compute_global_last_tick_exclusive();
+  INVARIANT(last_tick_ex <= computed_last_tick_ex);
+  INVARIANT(to_read_only_tick(last_tick_ex) > ro_tick_geq);
+#endif
+
+  // XXX: hacky
+  char rcu_guard[sizeof(scoped_rcu_base<false>)] = {0};
+  const size_t max_niters_with_rcu = 128;
+#define ENTER_RCU() \
+    do { \
+      new (&rcu_guard[0]) scoped_rcu_base<false>(); \
+    } while (0)
+#define EXIT_RCU() \
+    do { \
+      scoped_rcu_base<false> *px = (scoped_rcu_base<false> *) &rcu_guard[0]; \
+      px->~scoped_rcu_base<false>(); \
+    } while (0)
+
+  ctx.scratch_.empty_accept_from(ctx.queue_, ro_tick_geq);
+  ctx.scratch_.transfer_freelist(ctx.queue_);
+  px_queue &q = ctx.scratch_;
+  if (q.empty())
+    return;
+  bool in_rcu = false;
+  size_t niters_with_rcu = 0, n = 0;
+  for (auto it = q.begin(); it != q.end(); ++it, ++n, ++niters_with_rcu) {
+    auto &delent = *it;
+    INVARIANT(delent.tuple()->opaque.load(std::memory_order_acquire) == 1);
+    if (!delent.key_.get_flags()) {
+      // guaranteed to be gc-able now (even w/o RCU)
+#ifdef CHECK_INVARIANTS
+      if (delent.trigger_tid_ > last_consistent_tid /*|| !IsBlocked(delent.tuple_ahead_, delent.tuple(), last_consistent_tid) */) {
+        cerr << "tuple ahead     : " << g_proto_version_str(delent.tuple_ahead_->version) << endl;
+        cerr << "tuple ahead     : " << *delent.tuple_ahead_ << endl;
+        cerr << "trigger tid     : " << g_proto_version_str(delent.trigger_tid_) << endl;
+        cerr << "tuple           : " << g_proto_version_str(delent.tuple()->version) << endl;
+        cerr << "last_consist_tid: " << g_proto_version_str(last_consistent_tid) << endl;
+        cerr << "last_tick_ex    : " << last_tick_ex << endl;
+        cerr << "ro_tick_geq     : " << ro_tick_geq << endl;
+        cerr << "rcu_block_tick  : " << it.tick() << endl;
+      }
+      INVARIANT(delent.trigger_tid_ <= last_consistent_tid);
+      delent.tuple()->opaque.store(0, std::memory_order_release);
+#endif
+      dbtuple::release_no_rcu(delent.tuple());
+    } else {
+      INVARIANT(!delent.tuple_ahead_);
+      INVARIANT(delent.btr_);
+      // check if an element preceeds the (deleted) tuple before doing the delete
+      ::lock_guard<dbtuple> lg_tuple(delent.tuple(), false);
+#ifdef CHECK_INVARIANTS
+      if (!delent.tuple()->is_not_behind(last_consistent_tid)) {
+        cerr << "trigger tid     : " << g_proto_version_str(delent.trigger_tid_) << endl;
+        cerr << "tuple           : " << g_proto_version_str(delent.tuple()->version) << endl;
+        cerr << "last_consist_tid: " << g_proto_version_str(last_consistent_tid) << endl;
+        cerr << "last_tick_ex    : " << last_tick_ex << endl;
+        cerr << "ro_tick_geq     : " << ro_tick_geq << endl;
+        cerr << "rcu_block_tick  : " << it.tick() << endl;
+      }
+      INVARIANT(delent.tuple()->version == delent.trigger_tid_);
+      INVARIANT(delent.tuple()->is_not_behind(last_consistent_tid));
+      INVARIANT(delent.tuple()->is_deleting());
+#endif
+      if (unlikely(!delent.tuple()->is_latest())) {
+        // requeue it up, except this time as a regular delete
+        const uint64_t my_ro_tick = to_read_only_tick(
+            ticker::s_instance.global_current_tick());
+        ctx.queue_.enqueue(
+            delete_entry(
+              nullptr,
+              MakeTid(CoreMask, NumIdMask >> NumIdShift, (my_ro_tick + 1) * ReadOnlyEpochMultiplier - 1),
+              delent.tuple(),
+              marked_ptr<string>(),
+              nullptr),
+            my_ro_tick);
+        ++g_evt_proto_gc_delete_requeue;
+        // reclaim string ptrs
+        string *spx = delent.key_.get();
+        if (unlikely(spx))
+          ctx.pool_.emplace_back(spx);
+        continue;
+      }
+#ifdef CHECK_INVARIANTS
+      delent.tuple()->opaque.store(0, std::memory_order_release);
+#endif
+      // if delent.key_ is nullptr, then the key is stored in the tuple
+      // record storage location, and the size field contains the length of
+      // the key
+      //
+      // otherwise, delent.key_ is a pointer to a string containing the
+      // key
+      varkey k;
+      string *spx = delent.key_.get();
+      if (likely(!spx)) {
+        k = varkey(delent.tuple()->get_value_start(), delent.tuple()->size);
+      } else {
+        k = varkey(*spx);
+        ctx.pool_.emplace_back(spx);
+      }
+
+      if (!in_rcu) {
+        ENTER_RCU();
+        niters_with_rcu = 0;
+        in_rcu = true;
+      }
+      typename concurrent_btree::value_type removed = 0;
+      const bool did_remove = delent.btr_->remove(k, &removed);
+      ALWAYS_ASSERT(did_remove);
+      INVARIANT(removed == (typename concurrent_btree::value_type) delent.tuple());
+      delent.tuple()->clear_latest();
+      dbtuple::release(delent.tuple()); // rcu free it
+    }
+
+    if (in_rcu && niters_with_rcu >= max_niters_with_rcu) {
+      EXIT_RCU();
+      niters_with_rcu = 0;
+      in_rcu = false;
+    }
+  }
+  q.clear();
+  g_evt_avg_proto_gc_queue_len.offer(n);
+
+  if (in_rcu)
+    EXIT_RCU();
+  INVARIANT(!rcu::s_instance.in_rcu_region());
+}
+
+aligned_padded_elem<transaction_proto2_static::hackstruct>
+  transaction_proto2_static::g_hack;
+aligned_padded_elem<transaction_proto2_static::flags>
+  transaction_proto2_static::g_flags;
+percore_lazy<transaction_proto2_static::threadctx>
+  transaction_proto2_static::g_threadctxs;
+event_counter
+  transaction_proto2_static::g_evt_worker_thread_wait_log_buffer(
+      "worker_thread_wait_log_buffer");
+event_counter
+  transaction_proto2_static::g_evt_dbtuple_no_space_for_delkey(
+      "dbtuple_no_space_for_delkey");
+event_counter
+  transaction_proto2_static::g_evt_proto_gc_delete_requeue(
+      "proto_gc_delete_requeue");
+event_avg_counter
+  transaction_proto2_static::g_evt_avg_log_entry_size(
+      "avg_log_entry_size");
+event_avg_counter
+  transaction_proto2_static::g_evt_avg_proto_gc_queue_len(
+      "avg_proto_gc_queue_len");
diff --git a/silo/txn_proto2_impl.h b/silo/txn_proto2_impl.h
new file mode 100644 (file)
index 0000000..83021ab
--- /dev/null
@@ -0,0 +1,1265 @@
+#ifndef _NDB_TXN_PROTO2_IMPL_H_
+#define _NDB_TXN_PROTO2_IMPL_H_
+
+#include <iostream>
+#include <atomic>
+#include <vector>
+#include <set>
+
+#include <lz4.h>
+
+#include "txn.h"
+#include "txn_impl.h"
+#include "txn_btree.h"
+#include "macros.h"
+#include "circbuf.h"
+#include "spinbarrier.h"
+#include "record/serializer.h"
+
+// forward decl
+template <typename Traits> class transaction_proto2;
+template <template <typename> class Transaction>
+  class txn_epoch_sync;
+
+// the system has a single logging subsystem (composed of multiple lgogers)
+// NOTE: currently, the persistence epoch is tied 1:1 with the ticker's epoch
+class txn_logger {
+  friend class transaction_proto2_static;
+  template <typename T>
+    friend class transaction_proto2;
+  // XXX: should only allow txn_epoch_sync<transaction_proto2> as friend
+  template <template <typename> class T>
+    friend class txn_epoch_sync;
+public:
+
+  static const size_t g_nmax_loggers = 16;
+  static const size_t g_perthread_buffers = 256; // 256 outstanding buffers
+  static const size_t g_buffer_size = (1<<20); // in bytes
+  static const size_t g_horizon_buffer_size = 2 * (1<<16); // in bytes
+  static const size_t g_max_lag_epochs = 128; // cannot lag more than 128 epochs
+  static const bool   g_pin_loggers_to_numa_nodes = false;
+
+  static inline bool
+  IsPersistenceEnabled()
+  {
+    return g_persist;
+  }
+
+  static inline bool
+  IsCompressionEnabled()
+  {
+    return g_use_compression;
+  }
+
+  // init the logging subsystem.
+  //
+  // should only be called ONCE is not thread-safe.  if assignments_used is not
+  // null, then fills it with a copy of the assignment actually computed
+  static void Init(
+      size_t nworkers,
+      const std::vector<std::string> &logfiles,
+      const std::vector<std::vector<unsigned>> &assignments_given,
+      std::vector<std::vector<unsigned>> *assignments_used = nullptr,
+      bool call_fsync = true,
+      bool use_compression = false,
+      bool fake_writes = false);
+
+  struct logbuf_header {
+    uint64_t nentries_; // > 0 for all valid log buffers
+    uint64_t last_tid_; // TID of the last commit
+  } PACKED;
+
+  struct pbuffer {
+    uint64_t earliest_start_us_; // start time of the earliest txn
+    bool io_scheduled_; // has the logger scheduled IO yet?
+
+    unsigned curoff_; // current offset into buf_ for writing
+
+    const unsigned core_id_; // which core does this pbuffer belong to?
+
+    const unsigned buf_sz_;
+
+    // must be last field
+    uint8_t buf_start_[0];
+
+    // to allocate a pbuffer, use placement new:
+    //    const size_t bufsz = ...;
+    //    char *p = malloc(sizeof(pbuffer) + bufsz);
+    //    pbuffer *pb = new (p) pbuffer(core_id, bufsz);
+    //
+    // NOTE: it is not necessary to call the destructor for pbuffer, since
+    // it only contains PODs
+    pbuffer(unsigned core_id, unsigned buf_sz)
+      : core_id_(core_id), buf_sz_(buf_sz)
+    {
+      INVARIANT(((char *)this) + sizeof(*this) == (char *) &buf_start_[0]);
+      INVARIANT(buf_sz > sizeof(logbuf_header));
+      reset();
+    }
+
+    pbuffer(const pbuffer &) = delete;
+    pbuffer &operator=(const pbuffer &) = delete;
+    pbuffer(pbuffer &&) = delete;
+
+    inline void
+    reset()
+    {
+      earliest_start_us_ = 0;
+      io_scheduled_ = false;
+      curoff_ = sizeof(logbuf_header);
+      NDB_MEMSET(&buf_start_[0], 0, buf_sz_);
+    }
+
+    inline uint8_t *
+    pointer()
+    {
+      INVARIANT(curoff_ >= sizeof(logbuf_header));
+      INVARIANT(curoff_ <= buf_sz_);
+      return &buf_start_[0] + curoff_;
+    }
+
+    inline uint8_t *
+    datastart()
+    {
+      return &buf_start_[0] + sizeof(logbuf_header);
+    }
+
+    inline size_t
+    datasize() const
+    {
+      INVARIANT(curoff_ >= sizeof(logbuf_header));
+      INVARIANT(curoff_ <= buf_sz_);
+      return curoff_ - sizeof(logbuf_header);
+    }
+
+    inline logbuf_header *
+    header()
+    {
+      return reinterpret_cast<logbuf_header *>(&buf_start_[0]);
+    }
+
+    inline const logbuf_header *
+    header() const
+    {
+      return reinterpret_cast<const logbuf_header *>(&buf_start_[0]);
+    }
+
+    inline size_t
+    space_remaining() const
+    {
+      INVARIANT(curoff_ >= sizeof(logbuf_header));
+      INVARIANT(curoff_ <= buf_sz_);
+      return buf_sz_ - curoff_;
+    }
+
+    inline bool
+    can_hold_tid(uint64_t tid) const;
+  } PACKED;
+
+  static bool
+  AssignmentsValid(const std::vector<std::vector<unsigned>> &assignments,
+                   unsigned nfds,
+                   unsigned nworkers)
+  {
+    // each worker must be assigned exactly once in the assignment
+    // there must be <= nfds assignments
+
+    if (assignments.size() > nfds)
+      return false;
+
+    std::set<unsigned> seen;
+    for (auto &assignment : assignments)
+      for (auto w : assignment) {
+        if (seen.count(w) || w >= nworkers)
+          return false;
+        seen.insert(w);
+      }
+
+    return seen.size() == nworkers;
+  }
+
+  typedef circbuf<pbuffer, g_perthread_buffers> pbuffer_circbuf;
+
+  static std::tuple<uint64_t, uint64_t, double>
+  compute_ntxns_persisted_statistics();
+
+  // purge counters from each thread about the number of
+  // persisted txns
+  static void
+  clear_ntxns_persisted_statistics();
+
+  // wait until the logging system appears to be idle.
+  //
+  // note that this isn't a guarantee, just a best effort attempt
+  static void
+  wait_for_idle_state();
+
+  // waits until the epoch on invocation time is persisted
+  static void
+  wait_until_current_point_persisted();
+
+private:
+
+  // data structures
+
+  struct epoch_array {
+    // don't use percore<std::atomic<uint64_t>> because we don't want padding
+    std::atomic<uint64_t> epochs_[NMAXCORES];
+    std::atomic<uint64_t> dummy_work_; // so we can do some fake work
+    CACHE_PADOUT;
+  };
+
+  struct persist_ctx {
+    bool init_;
+
+    void *lz4ctx_;     // for compression
+    pbuffer *horizon_; // for compression
+
+    circbuf<pbuffer, g_perthread_buffers> all_buffers_;     // logger pushes to core
+    circbuf<pbuffer, g_perthread_buffers> persist_buffers_; // core pushes to logger
+
+    persist_ctx() : init_(false), lz4ctx_(nullptr), horizon_(nullptr) {}
+  };
+
+  // context per one epoch
+  struct persist_stats {
+    // how many txns this thread has persisted in total
+    std::atomic<uint64_t> ntxns_persisted_;
+
+    // how many txns have been pushed to the logger (but not necessarily persisted)
+    std::atomic<uint64_t> ntxns_pushed_;
+
+    // committed (but not necessarily pushed, nor persisted)
+    std::atomic<uint64_t> ntxns_committed_;
+
+    // sum of all latencies (divid by ntxns_persisted_ to get avg latency in
+    // us) for *persisted* txns (is conservative)
+    std::atomic<uint64_t> latency_numer_;
+
+    // per last g_max_lag_epochs information
+    struct per_epoch_stats {
+      std::atomic<uint64_t> ntxns_;
+      std::atomic<uint64_t> earliest_start_us_;
+
+      per_epoch_stats() : ntxns_(0), earliest_start_us_(0) {}
+    } d_[g_max_lag_epochs];
+
+    persist_stats() :
+      ntxns_persisted_(0), ntxns_pushed_(0),
+      ntxns_committed_(0), latency_numer_(0) {}
+  };
+
+  // helpers
+
+  static void
+  advance_system_sync_epoch(
+      const std::vector<std::vector<unsigned>> &assignments);
+
+  // makes copy on purpose
+  static void writer(
+      unsigned id, int fd,
+      std::vector<unsigned> assignment);
+
+  static void persister(
+      std::vector<std::vector<unsigned>> assignments);
+
+  enum InitMode {
+    INITMODE_NONE, // no initialization
+    INITMODE_REG,  // just use malloc() to init buffers
+    INITMODE_RCU,  // try to use the RCU numa aware allocator
+  };
+
+  static inline persist_ctx &
+  persist_ctx_for(uint64_t core_id, InitMode imode)
+  {
+    INVARIANT(core_id < g_persist_ctxs.size());
+    persist_ctx &ctx = g_persist_ctxs[core_id];
+    if (unlikely(!ctx.init_ && imode != INITMODE_NONE)) {
+      size_t needed = g_perthread_buffers * (sizeof(pbuffer) + g_buffer_size);
+      if (IsCompressionEnabled())
+        needed += size_t(LZ4_create_size()) +
+          sizeof(pbuffer) + g_horizon_buffer_size;
+      char *mem =
+        (imode == INITMODE_REG) ?
+          (char *) malloc(needed) :
+          (char *) rcu::s_instance.alloc_static(needed);
+      if (IsCompressionEnabled()) {
+        ctx.lz4ctx_ = mem;
+        mem += LZ4_create_size();
+        ctx.horizon_ = new (mem) pbuffer(core_id, g_horizon_buffer_size);
+        mem += sizeof(pbuffer) + g_horizon_buffer_size;
+      }
+      for (size_t i = 0; i < g_perthread_buffers; i++) {
+        ctx.all_buffers_.enq(new (mem) pbuffer(core_id, g_buffer_size));
+        mem += sizeof(pbuffer) + g_buffer_size;
+      }
+      ctx.init_ = true;
+    }
+    return ctx;
+  }
+
+  // static state
+
+  static bool g_persist; // whether or not logging is enabled
+
+  static bool g_call_fsync; // whether or not fsync() needs to be called
+                            // in order to be considered durable
+
+  static bool g_use_compression; // whether or not to compress log buffers
+
+  static bool g_fake_writes; // whether or not to fake doing writes (to measure
+                             // pure overhead of disk)
+
+  static size_t g_nworkers; // assignments are computed based on g_nworkers
+                            // but a logger responsible for core i is really
+                            // responsible for cores i + k * g_nworkers, for k
+                            // >= 0
+
+  // v = per_thread_sync_epochs_[i].epochs_[j]: logger i has persisted up
+  // through (including) all transactions <= epoch v on core j. since core =>
+  // logger mapping is static, taking:
+  //   min_{core} max_{logger} per_thread_sync_epochs_[logger].epochs_[core]
+  // yields the entire system's persistent epoch
+  static epoch_array
+    per_thread_sync_epochs_[g_nmax_loggers] CACHE_ALIGNED;
+
+  // conservative estimate (<=) for:
+  //   min_{core} max_{logger} per_thread_sync_epochs_[logger].epochs_[core]
+  static util::aligned_padded_elem<std::atomic<uint64_t>>
+    system_sync_epoch_ CACHE_ALIGNED;
+
+  static percore<persist_ctx> g_persist_ctxs CACHE_ALIGNED;
+
+  static percore<persist_stats> g_persist_stats CACHE_ALIGNED;
+
+  // counters
+
+  static event_counter g_evt_log_buffer_epoch_boundary;
+  static event_counter g_evt_log_buffer_out_of_space;
+  static event_counter g_evt_log_buffer_bytes_before_compress;
+  static event_counter g_evt_log_buffer_bytes_after_compress;
+  static event_counter g_evt_logger_writev_limit_met;
+  static event_counter g_evt_logger_max_lag_wait;
+  static event_avg_counter g_evt_avg_log_entry_ntxns;
+  static event_avg_counter g_evt_avg_log_buffer_compress_time_us;
+  static event_avg_counter g_evt_avg_logger_bytes_per_writev;
+  static event_avg_counter g_evt_avg_logger_bytes_per_sec;
+};
+
+static inline std::ostream &
+operator<<(std::ostream &o, txn_logger::logbuf_header &hdr)
+{
+  o << "{nentries_=" << hdr.nentries_ << ", last_tid_="
+    << g_proto_version_str(hdr.last_tid_) << "}";
+  return o;
+}
+
+class transaction_proto2_static {
+public:
+
+  // NOTE:
+  // each epoch is tied (1:1) to the ticker subsystem's tick. this is the
+  // speed of the persistence layer.
+  //
+  // however, read only txns and GC are tied to multiples of the ticker
+  // subsystem's tick
+
+#ifdef CHECK_INVARIANTS
+  static const uint64_t ReadOnlyEpochMultiplier = 10; /* 10 * 1 ms */
+#else
+  static const uint64_t ReadOnlyEpochMultiplier = 25; /* 25 * 40 ms */
+  static_assert(ticker::tick_us * ReadOnlyEpochMultiplier == 1000000, "");
+#endif
+
+  static_assert(ReadOnlyEpochMultiplier >= 1, "XX");
+
+  static const uint64_t ReadOnlyEpochUsec =
+    ticker::tick_us * ReadOnlyEpochMultiplier;
+
+  static inline uint64_t constexpr
+  to_read_only_tick(uint64_t epoch_tick)
+  {
+    return epoch_tick / ReadOnlyEpochMultiplier;
+  }
+
+  // in this protocol, the version number is:
+  // (note that for tid_t's, the top bit is reserved and
+  // *must* be set to zero
+  //
+  // [ core  | number |  epoch | reserved ]
+  // [ 0..9  | 9..33  | 33..63 |  63..64  ]
+
+  static inline ALWAYS_INLINE
+  uint64_t CoreId(uint64_t v)
+  {
+    return v & CoreMask;
+  }
+
+  static inline ALWAYS_INLINE
+  uint64_t NumId(uint64_t v)
+  {
+    return (v & NumIdMask) >> NumIdShift;
+  }
+
+  static inline ALWAYS_INLINE
+  uint64_t EpochId(uint64_t v)
+  {
+    return (v & EpochMask) >> EpochShift;
+  }
+
+  // XXX(stephentu): HACK
+  static void
+  wait_an_epoch()
+  {
+    INVARIANT(!rcu::s_instance.in_rcu_region());
+    const uint64_t e = to_read_only_tick(
+        ticker::s_instance.global_last_tick_exclusive());
+    if (!e) {
+      std::cerr << "wait_an_epoch(): consistent reads happening in e-1, but e=0 so special case"
+                << std::endl;
+    } else {
+      std::cerr << "wait_an_epoch(): consistent reads happening in e-1: "
+                << (e-1) << std::endl;
+    }
+    while (to_read_only_tick(ticker::s_instance.global_last_tick_exclusive()) == e)
+      nop_pause();
+    COMPILER_MEMORY_FENCE;
+  }
+
+  static uint64_t
+  ComputeReadOnlyTid(uint64_t global_tick_ex)
+  {
+    const uint64_t a = (global_tick_ex / ReadOnlyEpochMultiplier);
+    const uint64_t b = a * ReadOnlyEpochMultiplier;
+
+    // want to read entries <= b-1, special casing for b=0
+    if (!b)
+      return MakeTid(0, 0, 0);
+    else
+      return MakeTid(CoreMask, NumIdMask >> NumIdShift, b - 1);
+  }
+
+  static const uint64_t NBitsNumber = 24;
+
+  // XXX(stephentu): need to implement core ID recycling
+  static const size_t CoreBits = NMAXCOREBITS; // allow 2^CoreShift distinct threads
+  static const size_t NMaxCores = NMAXCORES;
+
+  static const uint64_t CoreMask = (NMaxCores - 1);
+
+  static const uint64_t NumIdShift = CoreBits;
+  static const uint64_t NumIdMask = ((((uint64_t)1) << NBitsNumber) - 1) << NumIdShift;
+
+  static const uint64_t EpochShift = CoreBits + NBitsNumber;
+  // since the reserve bit is always zero, we don't need a special mask
+  static const uint64_t EpochMask = ((uint64_t)-1) << EpochShift;
+
+  static inline ALWAYS_INLINE
+  uint64_t MakeTid(uint64_t core_id, uint64_t num_id, uint64_t epoch_id)
+  {
+    // some sanity checking
+    static_assert((CoreMask | NumIdMask | EpochMask) == ((uint64_t)-1), "xx");
+    static_assert((CoreMask & NumIdMask) == 0, "xx");
+    static_assert((NumIdMask & EpochMask) == 0, "xx");
+    return (core_id) | (num_id << NumIdShift) | (epoch_id << EpochShift);
+  }
+
+  static inline void
+  set_hack_status(bool hack_status)
+  {
+    g_hack->status_ = hack_status;
+  }
+
+  static inline bool
+  get_hack_status()
+  {
+    return g_hack->status_;
+  }
+
+  // thread-safe, can be called many times
+  static void InitGC();
+
+  static void PurgeThreadOutstandingGCTasks();
+
+#ifdef PROTO2_CAN_DISABLE_GC
+  static inline bool
+  IsGCEnabled()
+  {
+    return g_flags->g_gc_init.load(std::memory_order_acquire);
+  }
+#endif
+
+#ifdef PROTO2_CAN_DISABLE_SNAPSHOTS
+  static void
+  DisableSnapshots()
+  {
+    g_flags->g_disable_snapshots.store(true, std::memory_order_release);
+  }
+  static inline bool
+  IsSnapshotsEnabled()
+  {
+    return !g_flags->g_disable_snapshots.load(std::memory_order_acquire);
+  }
+#endif
+
+protected:
+  struct delete_entry {
+#ifdef CHECK_INVARIANTS
+    dbtuple *tuple_ahead_;
+    uint64_t trigger_tid_;
+#endif
+
+    dbtuple *tuple_;
+    marked_ptr<std::string> key_;
+    concurrent_btree *btr_;
+
+    delete_entry()
+      :
+#ifdef CHECK_INVARIANTS
+        tuple_ahead_(nullptr),
+        trigger_tid_(0),
+#endif
+        tuple_(),
+        key_(),
+        btr_(nullptr) {}
+
+    delete_entry(dbtuple *tuple_ahead,
+                 uint64_t trigger_tid,
+                 dbtuple *tuple,
+                 const marked_ptr<std::string> &key,
+                 concurrent_btree *btr)
+      :
+#ifdef CHECK_INVARIANTS
+        tuple_ahead_(tuple_ahead),
+        trigger_tid_(trigger_tid),
+#endif
+        tuple_(tuple),
+        key_(key),
+        btr_(btr) {}
+
+    inline dbtuple *
+    tuple()
+    {
+      return tuple_;
+    }
+  };
+
+  typedef basic_px_queue<delete_entry, 4096> px_queue;
+
+  struct threadctx {
+    uint64_t last_commit_tid_;
+    unsigned last_reaped_epoch_;
+#ifdef ENABLE_EVENT_COUNTERS
+    uint64_t last_reaped_timestamp_us_;
+#endif
+    px_queue queue_;
+    px_queue scratch_;
+    std::deque<std::string *> pool_;
+    threadctx() :
+        last_commit_tid_(0)
+      , last_reaped_epoch_(0)
+#ifdef ENABLE_EVENT_COUNTERS
+      , last_reaped_timestamp_us_(0)
+#endif
+    {
+      ALWAYS_ASSERT(((uintptr_t)this % CACHELINE_SIZE) == 0);
+      queue_.alloc_freelist(rcu::NQueueGroups);
+      scratch_.alloc_freelist(rcu::NQueueGroups);
+    }
+  };
+
+  static void
+  clean_up_to_including(threadctx &ctx, uint64_t ro_tick_geq);
+
+  // helper methods
+  static inline txn_logger::pbuffer *
+  wait_for_head(txn_logger::pbuffer_circbuf &pull_buf)
+  {
+    // XXX(stephentu): spinning for now
+    txn_logger::pbuffer *px;
+    while (unlikely(!(px = pull_buf.peek()))) {
+      nop_pause();
+      ++g_evt_worker_thread_wait_log_buffer;
+    }
+    INVARIANT(!px->io_scheduled_);
+    return px;
+  }
+
+  // pushes horizon to the front entry of pull_buf, pushing
+  // to push_buf if necessary
+  //
+  // horizon is reset after push_horizon_to_buffer() returns
+  //
+  // returns the number of txns pushed from buffer to *logger*
+  // (if doing so was necessary)
+  static inline size_t
+  push_horizon_to_buffer(txn_logger::pbuffer *horizon,
+                         void *lz4ctx,
+                         txn_logger::pbuffer_circbuf &pull_buf,
+                         txn_logger::pbuffer_circbuf &push_buf)
+  {
+    INVARIANT(txn_logger::IsCompressionEnabled());
+    if (unlikely(!horizon->header()->nentries_))
+      return 0;
+    INVARIANT(horizon->datasize());
+
+    size_t ntxns_pushed_to_logger = 0;
+
+    // horizon out of space- try to push horizon to buffer
+    txn_logger::pbuffer *px = wait_for_head(pull_buf);
+    const uint64_t compressed_space_needed =
+      sizeof(uint32_t) + LZ4_compressBound(horizon->datasize());
+
+    bool buffer_cond = false;
+    if (px->space_remaining() < compressed_space_needed ||
+        (buffer_cond = !px->can_hold_tid(horizon->header()->last_tid_))) {
+      // buffer out of space- push buffer to logger
+      INVARIANT(px->header()->nentries_);
+      ntxns_pushed_to_logger = px->header()->nentries_;
+      txn_logger::pbuffer *px1 = pull_buf.deq();
+      INVARIANT(px == px1);
+      push_buf.enq(px1);
+      px = wait_for_head(pull_buf);
+      if (buffer_cond)
+        ++txn_logger::g_evt_log_buffer_epoch_boundary;
+      else
+        ++txn_logger::g_evt_log_buffer_out_of_space;
+    }
+
+    INVARIANT(px->space_remaining() >= compressed_space_needed);
+    if (!px->header()->nentries_)
+      px->earliest_start_us_ = horizon->earliest_start_us_;
+    px->header()->nentries_ += horizon->header()->nentries_;
+    px->header()->last_tid_  = horizon->header()->last_tid_;
+
+#ifdef ENABLE_EVENT_COUNTERS
+    util::timer tt;
+#endif
+    const int ret = LZ4_compress_heap_limitedOutput(
+        lz4ctx,
+        (const char *) horizon->datastart(),
+        (char *) px->pointer() + sizeof(uint32_t),
+        horizon->datasize(),
+        px->space_remaining() - sizeof(uint32_t));
+#ifdef ENABLE_EVENT_COUNTERS
+    txn_logger::g_evt_avg_log_buffer_compress_time_us.offer(tt.lap());
+    txn_logger::g_evt_log_buffer_bytes_before_compress.inc(horizon->datasize());
+    txn_logger::g_evt_log_buffer_bytes_after_compress.inc(ret);
+#endif
+    INVARIANT(ret > 0);
+#if defined(CHECK_INVARIANTS) && defined(PARANOID_CHECKING)
+    {
+      uint8_t decode_buf[txn_logger::g_horizon_buffer_size];
+      const int decode_ret =
+        LZ4_decompress_safe_partial(
+            (const char *) px->pointer() + sizeof(uint32_t),
+            (char *) &decode_buf[0],
+            ret,
+            txn_logger::g_horizon_buffer_size,
+            txn_logger::g_horizon_buffer_size);
+      INVARIANT(decode_ret >= 0);
+      INVARIANT(size_t(decode_ret) == horizon->datasize());
+      INVARIANT(memcmp(horizon->datastart(),
+                       &decode_buf[0], decode_ret) == 0);
+    }
+#endif
+
+    serializer<uint32_t, false> s_uint32_t;
+    s_uint32_t.write(px->pointer(), ret);
+    px->curoff_ += sizeof(uint32_t) + uint32_t(ret);
+    horizon->reset();
+
+    return ntxns_pushed_to_logger;
+  }
+
+  struct hackstruct {
+    std::atomic<bool> status_;
+    std::atomic<uint64_t> global_tid_;
+    constexpr hackstruct() : status_(false), global_tid_(0) {}
+  };
+
+  // use to simulate global TID for comparsion
+  static util::aligned_padded_elem<hackstruct>
+    g_hack CACHE_ALIGNED;
+
+  struct flags {
+    std::atomic<bool> g_gc_init;
+    std::atomic<bool> g_disable_snapshots;
+    constexpr flags() : g_gc_init(false), g_disable_snapshots(false) {}
+  };
+  static util::aligned_padded_elem<flags> g_flags;
+
+  static percore_lazy<threadctx> g_threadctxs;
+
+  static event_counter g_evt_worker_thread_wait_log_buffer;
+  static event_counter g_evt_dbtuple_no_space_for_delkey;
+  static event_counter g_evt_proto_gc_delete_requeue;
+  static event_avg_counter g_evt_avg_log_entry_size;
+  static event_avg_counter g_evt_avg_proto_gc_queue_len;
+};
+
+bool
+txn_logger::pbuffer::can_hold_tid(uint64_t tid) const
+{
+  return !header()->nentries_ ||
+         (transaction_proto2_static::EpochId(header()->last_tid_) ==
+          transaction_proto2_static::EpochId(tid));
+}
+
+// protocol 2 - no global consistent TIDs
+template <typename Traits>
+class transaction_proto2 : public transaction<transaction_proto2, Traits>,
+                           private transaction_proto2_static {
+
+  friend class transaction<transaction_proto2, Traits>;
+  typedef transaction<transaction_proto2, Traits> super_type;
+
+public:
+
+  typedef Traits traits_type;
+  typedef transaction_base::tid_t tid_t;
+  typedef transaction_base::string_type string_type;
+  typedef typename super_type::dbtuple_write_info dbtuple_write_info;
+  typedef typename super_type::dbtuple_write_info_vec dbtuple_write_info_vec;
+  typedef typename super_type::read_set_map read_set_map;
+  typedef typename super_type::absent_set_map absent_set_map;
+  typedef typename super_type::write_set_map write_set_map;
+  typedef typename super_type::write_set_u32_vec write_set_u32_vec;
+
+  transaction_proto2(uint64_t flags,
+                     typename Traits::StringAllocator &sa)
+    : transaction<transaction_proto2, Traits>(flags, sa)
+  {
+    if (this->get_flags() & transaction_base::TXN_FLAG_READ_ONLY) {
+      const uint64_t global_tick_ex =
+        this->rcu_guard_->guard()->impl().global_last_tick_exclusive();
+      u_.last_consistent_tid = ComputeReadOnlyTid(global_tick_ex);
+    }
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+    dbtuple::TupleLockRegionBegin();
+#endif
+    INVARIANT(rcu::s_instance.in_rcu_region());
+  }
+
+  ~transaction_proto2()
+  {
+#ifdef TUPLE_LOCK_OWNERSHIP_CHECKING
+    dbtuple::AssertAllTupleLocksReleased();
+#endif
+    INVARIANT(rcu::s_instance.in_rcu_region());
+  }
+
+  inline bool
+  can_overwrite_record_tid(tid_t prev, tid_t cur) const
+  {
+    INVARIANT(prev <= cur);
+
+#ifdef PROTO2_CAN_DISABLE_SNAPSHOTS
+    if (!IsSnapshotsEnabled())
+      return true;
+#endif
+
+    // XXX(stephentu): the !prev check is a *bit* of a hack-
+    // we're assuming that !prev (MIN_TID) corresponds to an
+    // absent (removed) record, so it is safe to overwrite it,
+    //
+    // This is an OK assumption with *no TID wrap around*.
+    return (to_read_only_tick(EpochId(prev)) ==
+            to_read_only_tick(EpochId(cur))) ||
+           !prev;
+  }
+
+  // can only read elements in this epoch or previous epochs
+  inline bool
+  can_read_tid(tid_t t) const
+  {
+    return true;
+  }
+
+  inline void
+  on_tid_finish(tid_t commit_tid)
+  {
+    if (!txn_logger::IsPersistenceEnabled() ||
+        this->state != transaction_base::TXN_COMMITED)
+      return;
+    // need to write into log buffer
+
+    serializer<uint32_t, true> vs_uint32_t;
+
+    // compute how much space is necessary
+    uint64_t space_needed = 0;
+
+    // 8 bytes to indicate TID
+    space_needed += sizeof(uint64_t);
+
+    // variable bytes to indicate # of records written
+#ifdef LOGGER_UNSAFE_FAKE_COMPRESSION
+    const unsigned nwrites = 0;
+#else
+    const unsigned nwrites = this->write_set.size();
+#endif
+
+    space_needed += vs_uint32_t.nbytes(&nwrites);
+
+    // each record needs to be recorded
+    write_set_u32_vec value_sizes;
+    for (unsigned idx = 0; idx < nwrites; idx++) {
+      const transaction_base::write_record_t &rec = this->write_set[idx];
+      const uint32_t k_nbytes = rec.get_key().size();
+      space_needed += vs_uint32_t.nbytes(&k_nbytes);
+      space_needed += k_nbytes;
+
+      const uint32_t v_nbytes = rec.get_value() ?
+          rec.get_writer()(
+              dbtuple::TUPLE_WRITER_COMPUTE_DELTA_NEEDED,
+              rec.get_value(), nullptr, 0) : 0;
+      space_needed += vs_uint32_t.nbytes(&v_nbytes);
+      space_needed += v_nbytes;
+
+      value_sizes.push_back(v_nbytes);
+    }
+
+    g_evt_avg_log_entry_size.offer(space_needed);
+    INVARIANT(space_needed <= txn_logger::g_horizon_buffer_size);
+    INVARIANT(space_needed <= txn_logger::g_buffer_size);
+
+    const unsigned long my_core_id = coreid::core_id();
+
+    txn_logger::persist_ctx &ctx =
+      txn_logger::persist_ctx_for(my_core_id, txn_logger::INITMODE_REG);
+    txn_logger::persist_stats &stats =
+      txn_logger::g_persist_stats[my_core_id];
+    txn_logger::pbuffer_circbuf &pull_buf = ctx.all_buffers_;
+    txn_logger::pbuffer_circbuf &push_buf = ctx.persist_buffers_;
+
+    util::non_atomic_fetch_add(stats.ntxns_committed_, 1UL);
+
+    const bool do_compress = txn_logger::IsCompressionEnabled();
+    if (do_compress) {
+      // try placing in horizon
+      bool horizon_cond = false;
+      if (ctx.horizon_->space_remaining() < space_needed ||
+          (horizon_cond = !ctx.horizon_->can_hold_tid(commit_tid))) {
+        if (!ctx.horizon_->datasize()) {
+          std::cerr << "space_needed: " << space_needed << std::endl;
+          std::cerr << "space_remaining: " << ctx.horizon_->space_remaining() << std::endl;
+          std::cerr << "can_hold_tid: " << ctx.horizon_->can_hold_tid(commit_tid) << std::endl;
+        }
+        INVARIANT(ctx.horizon_->datasize());
+        // horizon out of space, so we push it
+        const uint64_t npushed =
+          push_horizon_to_buffer(ctx.horizon_, ctx.lz4ctx_, pull_buf, push_buf);
+        if (npushed)
+          util::non_atomic_fetch_add(stats.ntxns_pushed_, npushed);
+      }
+
+      INVARIANT(ctx.horizon_->space_remaining() >= space_needed);
+      const uint64_t written =
+        write_current_txn_into_buffer(ctx.horizon_, commit_tid, value_sizes);
+      if (written != space_needed)
+        INVARIANT(false);
+
+    } else {
+
+    retry:
+      txn_logger::pbuffer *px = wait_for_head(pull_buf);
+      INVARIANT(px && px->core_id_ == my_core_id);
+      bool cond = false;
+      if (px->space_remaining() < space_needed ||
+          (cond = !px->can_hold_tid(commit_tid))) {
+        INVARIANT(px->header()->nentries_);
+        txn_logger::pbuffer *px0 = pull_buf.deq();
+        INVARIANT(px == px0);
+        INVARIANT(px0->header()->nentries_);
+        util::non_atomic_fetch_add(stats.ntxns_pushed_, px0->header()->nentries_);
+        push_buf.enq(px0);
+        if (cond)
+          ++txn_logger::g_evt_log_buffer_epoch_boundary;
+        else
+          ++txn_logger::g_evt_log_buffer_out_of_space;
+        goto retry;
+      }
+
+      const uint64_t written =
+        write_current_txn_into_buffer(px, commit_tid, value_sizes);
+      if (written != space_needed)
+        INVARIANT(false);
+    }
+  }
+
+private:
+
+  // assumes enough space in px to hold this txn
+  inline uint64_t
+  write_current_txn_into_buffer(
+      txn_logger::pbuffer *px,
+      uint64_t commit_tid,
+      const write_set_u32_vec &value_sizes)
+  {
+    INVARIANT(px->can_hold_tid(commit_tid));
+
+    if (unlikely(!px->header()->nentries_))
+      px->earliest_start_us_ = this->rcu_guard_->guard()->start_us();
+
+    uint8_t *p = px->pointer();
+    uint8_t *porig = p;
+
+    serializer<uint32_t, true> vs_uint32_t;
+    serializer<uint64_t, false> s_uint64_t;
+
+#ifdef LOGGER_UNSAFE_FAKE_COMPRESSION
+    const unsigned nwrites = 0;
+#else
+    const unsigned nwrites = this->write_set.size();
+#endif
+
+
+    INVARIANT(nwrites == value_sizes.size());
+
+    p = s_uint64_t.write(p, commit_tid);
+    p = vs_uint32_t.write(p, nwrites);
+
+    for (unsigned idx = 0; idx < nwrites; idx++) {
+      const transaction_base::write_record_t &rec = this->write_set[idx];
+      const uint32_t k_nbytes = rec.get_key().size();
+      p = vs_uint32_t.write(p, k_nbytes);
+      NDB_MEMCPY(p, rec.get_key().data(), k_nbytes);
+      p += k_nbytes;
+      const uint32_t v_nbytes = value_sizes[idx];
+      p = vs_uint32_t.write(p, v_nbytes);
+      if (v_nbytes) {
+        rec.get_writer()(dbtuple::TUPLE_WRITER_DO_DELTA_WRITE, rec.get_value(), p, v_nbytes);
+        p += v_nbytes;
+      }
+    }
+
+    px->curoff_ += (p - porig);
+    px->header()->nentries_++;
+    px->header()->last_tid_ = commit_tid;
+
+    return uint64_t(p - porig);
+  }
+
+public:
+
+  inline ALWAYS_INLINE bool is_snapshot() const {
+    return this->get_flags() & transaction_base::TXN_FLAG_READ_ONLY;
+  }
+
+  inline transaction_base::tid_t
+  snapshot_tid() const
+  {
+#ifdef PROTO2_CAN_DISABLE_SNAPSHOTS
+    if (!IsSnapshotsEnabled())
+      // when snapshots are disabled, but we have a RO txn, we simply allow
+      // it to read all the latest values and treat them as consistent
+      //
+      // it's not correct, but its for the factor analysis
+      return dbtuple::MAX_TID;
+#endif
+    return u_.last_consistent_tid;
+  }
+
+  void
+  dump_debug_info() const
+  {
+    transaction<transaction_proto2, Traits>::dump_debug_info();
+    if (this->is_snapshot())
+      std::cerr << "  last_consistent_tid: "
+        << g_proto_version_str(u_.last_consistent_tid) << std::endl;
+  }
+
+  transaction_base::tid_t
+  gen_commit_tid(const dbtuple_write_info_vec &write_tuples)
+  {
+    const size_t my_core_id = this->rcu_guard_->guard()->core();
+    threadctx &ctx = g_threadctxs.get(my_core_id);
+    INVARIANT(!this->is_snapshot());
+
+    COMPILER_MEMORY_FENCE;
+    u_.commit_epoch = ticker::s_instance.global_current_tick();
+    COMPILER_MEMORY_FENCE;
+
+    tid_t ret = ctx.last_commit_tid_;
+    INVARIANT(ret == dbtuple::MIN_TID || CoreId(ret) == my_core_id);
+    if (u_.commit_epoch != EpochId(ret))
+      ret = MakeTid(0, 0, u_.commit_epoch);
+
+    // What is this? Is txn_proto1_impl used?
+    if (g_hack->status_.load(std::memory_order_acquire))
+      g_hack->global_tid_.fetch_add(1, std::memory_order_acq_rel);
+
+    // XXX(stephentu): I believe this is correct, but not 100% sure
+    //const size_t my_core_id = 0;
+    //tid_t ret = 0;
+    {
+      typename read_set_map::const_iterator it     = this->read_set.begin();
+      typename read_set_map::const_iterator it_end = this->read_set.end();
+      for (; it != it_end; ++it) {
+        if (it->get_tid() > ret)
+          ret = it->get_tid();
+      }
+    }
+
+    {
+      typename dbtuple_write_info_vec::const_iterator it     = write_tuples.begin();
+      typename dbtuple_write_info_vec::const_iterator it_end = write_tuples.end();
+      for (; it != it_end; ++it) {
+        INVARIANT(it->tuple->is_locked());
+        INVARIANT(it->tuple->is_lock_owner());
+        INVARIANT(it->tuple->is_write_intent());
+        INVARIANT(!it->tuple->is_modifying());
+        INVARIANT(it->tuple->is_latest());
+        if (it->is_insert())
+          // we inserted this node, so we don't want to do the checks below
+          continue;
+        const tid_t t = it->tuple->version;
+
+        // XXX(stephentu): we are overly conservative for now- technically this
+        // abort isn't necessary (we really should just write the value in the correct
+        // position)
+        //if (EpochId(t) > u_.commit_epoch) {
+        //  std::cerr << "t: " << g_proto_version_str(t) << std::endl;
+        //  std::cerr << "epoch: " << u_.commit_epoch << std::endl;
+        //  this->dump_debug_info();
+        //}
+
+        // t == dbtuple::MAX_TID when a txn does an insert of a new tuple
+        // followed by 1+ writes to the same tuple.
+        INVARIANT(EpochId(t) <= u_.commit_epoch || t == dbtuple::MAX_TID);
+        if (t != dbtuple::MAX_TID && t > ret)
+          ret = t;
+      }
+
+      INVARIANT(EpochId(ret) == u_.commit_epoch);
+      ret = MakeTid(my_core_id, NumId(ret) + 1, u_.commit_epoch);
+    }
+
+    // XXX(stephentu): this txn hasn't actually been commited yet,
+    // and could potentially be aborted - but it's ok to increase this #, since
+    // subsequent txns on this core will read this # anyways
+    return (ctx.last_commit_tid_ = ret);
+  }
+
+  inline ALWAYS_INLINE void
+  on_dbtuple_spill(dbtuple *tuple_ahead, dbtuple *tuple)
+  {
+#ifdef PROTO2_CAN_DISABLE_GC
+    if (!IsGCEnabled())
+      return;
+#endif
+
+    INVARIANT(rcu::s_instance.in_rcu_region());
+    INVARIANT(!tuple->is_latest());
+
+    // >= not > only b/c of the special case of inserting a new tuple +
+    // overwriting the newly inserted record with a longer sequence of bytes in
+    // the *same* txn
+    INVARIANT(tuple_ahead->version >= tuple->version);
+
+    if (tuple->is_deleting()) {
+      INVARIANT(tuple->is_locked());
+      INVARIANT(tuple->is_lock_owner());
+      // already on queue
+      return;
+    }
+
+    const uint64_t ro_tick = to_read_only_tick(this->u_.commit_epoch);
+    INVARIANT(to_read_only_tick(EpochId(tuple->version)) <= ro_tick);
+
+#ifdef CHECK_INVARIANTS
+    uint64_t exp = 0;
+    INVARIANT(tuple->opaque.compare_exchange_strong(exp, 1, std::memory_order_acq_rel));
+#endif
+
+    // when all snapshots are happening >= the current epoch,
+    // then we can safely remove tuple
+    threadctx &ctx = g_threadctxs.my();
+    ctx.queue_.enqueue(
+        delete_entry(tuple_ahead, tuple_ahead->version,
+          tuple, marked_ptr<std::string>(), nullptr),
+        ro_tick);
+  }
+
+  inline ALWAYS_INLINE void
+  on_logical_delete(dbtuple *tuple, const std::string &key, concurrent_btree *btr)
+  {
+#ifdef PROTO2_CAN_DISABLE_GC
+    if (!IsGCEnabled())
+      return;
+#endif
+
+    INVARIANT(tuple->is_locked());
+    INVARIANT(tuple->is_lock_owner());
+    INVARIANT(tuple->is_write_intent());
+    INVARIANT(tuple->is_latest());
+    INVARIANT(tuple->is_deleting());
+    INVARIANT(!tuple->size);
+    INVARIANT(rcu::s_instance.in_rcu_region());
+
+    const uint64_t ro_tick = to_read_only_tick(this->u_.commit_epoch);
+    threadctx &ctx = g_threadctxs.my();
+
+#ifdef CHECK_INVARIANTS
+    uint64_t exp = 0;
+    INVARIANT(tuple->opaque.compare_exchange_strong(exp, 1, std::memory_order_acq_rel));
+#endif
+
+    if (likely(key.size() <= tuple->alloc_size)) {
+      NDB_MEMCPY(tuple->get_value_start(), key.data(), key.size());
+      tuple->size = key.size();
+
+      // eligible for deletion when all snapshots >= the current epoch
+      marked_ptr<std::string> mpx;
+      mpx.set_flags(0x1);
+
+      ctx.queue_.enqueue(
+          delete_entry(nullptr, tuple->version, tuple, mpx, btr),
+          ro_tick);
+    } else {
+      // this is a rare event
+      ++g_evt_dbtuple_no_space_for_delkey;
+      std::string *spx = nullptr;
+      if (ctx.pool_.empty()) {
+        spx = new std::string(key.data(), key.size()); // XXX: use numa memory?
+      } else {
+        spx = ctx.pool_.front();
+        ctx.pool_.pop_front();
+        spx->assign(key.data(), key.size());
+      }
+      INVARIANT(spx);
+
+      marked_ptr<std::string> mpx(spx);
+      mpx.set_flags(0x1);
+
+      ctx.queue_.enqueue(
+          delete_entry(nullptr, tuple->version, tuple, mpx, btr),
+          ro_tick);
+    }
+  }
+
+  void
+  on_post_rcu_region_completion()
+  {
+#ifdef PROTO2_CAN_DISABLE_GC
+    if (!IsGCEnabled())
+      return;
+#endif
+    const uint64_t last_tick_ex = ticker::s_instance.global_last_tick_exclusive();
+    if (unlikely(!last_tick_ex))
+      return;
+    // we subtract one from the global last tick, because of the way
+    // consistent TIDs are computed, the global_last_tick_exclusive() can
+    // increase by at most one tick during a transaction.
+    const uint64_t ro_tick_ex = to_read_only_tick(last_tick_ex - 1);
+    if (unlikely(!ro_tick_ex))
+      // won't have anything to clean
+      return;
+    // all reads happening at >= ro_tick_geq
+    const uint64_t ro_tick_geq = ro_tick_ex - 1;
+    threadctx &ctx = g_threadctxs.my();
+    clean_up_to_including(ctx, ro_tick_geq);
+  }
+
+private:
+
+  union {
+    // the global epoch this txn is running in (this # is read when it starts)
+    // -- snapshot txns only
+    uint64_t last_consistent_tid;
+    // the epoch for this txn -- committing non-snapshot txns only
+    uint64_t commit_epoch;
+  } u_;
+};
+
+// txn_btree_handler specialization
+template <>
+struct base_txn_btree_handler<transaction_proto2> {
+  static inline void
+  on_construct()
+  {
+#ifndef PROTO2_CAN_DISABLE_GC
+    transaction_proto2_static::InitGC();
+#endif
+  }
+  static const bool has_background_task = true;
+};
+
+template <>
+struct txn_epoch_sync<transaction_proto2> : public transaction_proto2_static {
+  static void
+  sync()
+  {
+    wait_an_epoch();
+    if (txn_logger::IsPersistenceEnabled())
+      txn_logger::wait_until_current_point_persisted();
+  }
+  static void
+  finish()
+  {
+    if (txn_logger::IsPersistenceEnabled())
+      txn_logger::wait_until_current_point_persisted();
+  }
+  static void
+  thread_init(bool loader)
+  {
+    if (!txn_logger::IsPersistenceEnabled())
+      return;
+    const unsigned long my_core_id = coreid::core_id();
+    // try to initialize using numa allocator
+    txn_logger::persist_ctx_for(
+        my_core_id,
+        loader ? txn_logger::INITMODE_REG : txn_logger::INITMODE_RCU);
+  }
+  static void
+  thread_end()
+  {
+    if (!txn_logger::IsPersistenceEnabled())
+      return;
+    const unsigned long my_core_id = coreid::core_id();
+    txn_logger::persist_ctx &ctx =
+      txn_logger::persist_ctx_for(my_core_id, txn_logger::INITMODE_NONE);
+    if (unlikely(!ctx.init_))
+      return;
+    txn_logger::persist_stats &stats =
+      txn_logger::g_persist_stats[my_core_id];
+    txn_logger::pbuffer_circbuf &pull_buf = ctx.all_buffers_;
+    txn_logger::pbuffer_circbuf &push_buf = ctx.persist_buffers_;
+    if (txn_logger::IsCompressionEnabled() &&
+        ctx.horizon_->header()->nentries_) {
+      INVARIANT(ctx.horizon_->datasize());
+      const uint64_t npushed =
+        push_horizon_to_buffer(ctx.horizon_, ctx.lz4ctx_, pull_buf, push_buf);
+      if (npushed)
+        util::non_atomic_fetch_add(stats.ntxns_pushed_, npushed);
+    }
+    txn_logger::pbuffer *px = pull_buf.peek();
+    if (!px || !px->header()->nentries_) {
+      //std::cerr << "core " << my_core_id
+      //          << " nothing to push to logger" << std::endl;
+      return;
+    }
+    //std::cerr << "core " << my_core_id
+    //          << " pushing buffer to logger" << std::endl;
+    txn_logger::pbuffer *px0 = pull_buf.deq();
+    util::non_atomic_fetch_add(stats.ntxns_pushed_, px0->header()->nentries_);
+    INVARIANT(px0 == px);
+    push_buf.enq(px0);
+  }
+  static std::tuple<uint64_t, uint64_t, double>
+  compute_ntxn_persisted()
+  {
+    if (!txn_logger::IsPersistenceEnabled())
+      return std::make_tuple(0, 0, 0.0);
+    return txn_logger::compute_ntxns_persisted_statistics();
+  }
+  static void
+  reset_ntxn_persisted()
+  {
+    if (!txn_logger::IsPersistenceEnabled())
+      return;
+    txn_logger::clear_ntxns_persisted_statistics();
+  }
+};
+
+#endif /* _NDB_TXN_PROTO2_IMPL_H_ */
diff --git a/silo/typed_txn_btree.h b/silo/typed_txn_btree.h
new file mode 100644 (file)
index 0000000..e3a62e2
--- /dev/null
@@ -0,0 +1,593 @@
+#ifndef _NDB_TYPED_TXN_BTREE_H_
+#define _NDB_TYPED_TXN_BTREE_H_
+
+#include "base_txn_btree.h"
+#include "txn_btree.h"
+#include "record/cursor.h"
+
+template <typename Schema>
+struct typed_txn_btree_ {
+
+  typedef typename Schema::base_type base_type;
+  typedef typename Schema::key_type key_type;
+  typedef typename Schema::value_type value_type;
+  typedef typename Schema::value_descriptor_type value_descriptor_type;
+  typedef typename Schema::key_encoder_type key_encoder_type;
+  typedef typename Schema::value_encoder_type value_encoder_type;
+
+  static_assert(value_descriptor_type::nfields() <= 64, "xx");
+  static const uint64_t AllFieldsMask = (1UL << value_descriptor_type::nfields()) - 1;
+
+  static inline constexpr bool
+  IsAllFields(uint64_t m)
+  {
+    return (m & AllFieldsMask) == AllFieldsMask;
+  }
+
+  class key_reader {
+  public:
+    constexpr key_reader(bool no_key_results) : no_key_results(no_key_results) {}
+    inline const key_type &
+    operator()(const std::string &s)
+    {
+      const typename Schema::key_encoder_type key_encoder;
+      if (!no_key_results)
+        key_encoder.read(s, &k);
+      return k;
+    }
+#if NDB_MASSTREE
+    inline const key_type &
+    operator()(lcdf::Str s)
+    {
+      const typename Schema::key_encoder_type key_encoder;
+      if (!no_key_results)
+        key_encoder.read(s, &k);
+      return k;
+    }
+#endif
+  private:
+    key_type k;
+    bool no_key_results;
+  };
+
+  static inline bool
+  do_record_read(const uint8_t *data, size_t sz, uint64_t fields_mask, value_type *v)
+  {
+    if (IsAllFields(fields_mask)) {
+      // read the entire record
+      const value_encoder_type value_encoder;
+      return value_encoder.failsafe_read(data, sz, v);
+    } else {
+      // pick individual fields
+      read_record_cursor<base_type> r(data, sz);
+      for (uint64_t i = 0; i < value_descriptor_type::nfields(); i++) {
+        if ((1UL << i) & fields_mask) {
+          r.skip_to(i);
+          if (unlikely(!r.read_current_and_advance(v)))
+            return false;
+        }
+      }
+      return true;
+    }
+  }
+
+  class single_value_reader {
+  public:
+    typedef typename Schema::value_type value_type;
+
+    constexpr single_value_reader(value_type &v, uint64_t fields_mask)
+      : v(&v), fields_mask(fields_mask) {}
+
+    template <typename StringAllocator>
+    inline bool
+    operator()(const uint8_t *data, size_t sz, StringAllocator &sa)
+    {
+      return do_record_read(data, sz, fields_mask, v);
+    }
+
+    inline value_type &
+    results()
+    {
+      return *v;
+    }
+
+    inline const value_type &
+    results() const
+    {
+      return *v;
+    }
+
+    template <typename StringAllocator>
+    inline void
+    dup(const value_type &vdup, StringAllocator &sa)
+    {
+      *v = vdup;
+    }
+
+  private:
+    value_type *v;
+    uint64_t fields_mask;
+  };
+
+  class value_reader {
+  public:
+    typedef typename Schema::value_type value_type;
+
+    constexpr value_reader(uint64_t fields_mask) : fields_mask(fields_mask) {}
+
+    template <typename StringAllocator>
+    inline bool
+    operator()(const uint8_t *data, size_t sz, StringAllocator &sa)
+    {
+      return do_record_read(data, sz, fields_mask, &v);
+    }
+
+    inline value_type &
+    results()
+    {
+      return v;
+    }
+
+    inline const value_type &
+    results() const
+    {
+      return v;
+    }
+
+    template <typename StringAllocator>
+    inline void
+    dup(const value_type &vdup, StringAllocator &sa)
+    {
+      v = vdup;
+    }
+
+  private:
+    value_type v;
+    uint64_t fields_mask;
+  };
+
+  class key_writer {
+  public:
+    constexpr key_writer(const key_type *k) : k(k) {}
+
+    template <typename StringAllocator>
+    inline const std::string *
+    fully_materialize(bool stable_input, StringAllocator &sa)
+    {
+      if (!k)
+        return nullptr;
+      std::string * const ret = sa();
+      const key_encoder_type key_encoder;
+      key_encoder.write(*ret, k);
+      return ret;
+    }
+  private:
+    const key_type *k;
+  };
+
+  static inline size_t
+  compute_needed_standalone(
+    const value_type *v, uint64_t fields,
+    const uint8_t *buf, size_t sz)
+  {
+    if (fields == 0) {
+      // delete
+      INVARIANT(!v);
+      return 0;
+    }
+    INVARIANT(v);
+    if (sz == 0) {
+      // new record (insert)
+      INVARIANT(IsAllFields(fields));
+      const value_encoder_type value_encoder;
+      return value_encoder.nbytes(v);
+    }
+
+    ssize_t new_updates_sum = 0;
+    for (uint64_t i = 0; i < value_descriptor_type::nfields(); i++) {
+      if ((1UL << i) & fields) {
+        const uint8_t * px = reinterpret_cast<const uint8_t *>(v) +
+          value_descriptor_type::cstruct_offsetof(i);
+        new_updates_sum += value_descriptor_type::nbytes_fn(i)(px);
+      }
+    }
+
+    // XXX: should try to cache pointers discovered by read_record_cursor
+    ssize_t old_updates_sum = 0;
+    read_record_cursor<base_type> rc(buf, sz);
+    for (uint64_t i = 0; i < value_descriptor_type::nfields(); i++) {
+      if ((1UL << i) & fields) {
+        rc.skip_to(i);
+        const size_t sz = rc.read_current_raw_size_and_advance();
+        INVARIANT(sz);
+        old_updates_sum += sz;
+      }
+    }
+
+    // XXX: see if approximate version works almost as well (approx version is
+    // to assume that each field has the minimum possible size, which is
+    // overly conservative but correct)
+
+    const ssize_t ret = static_cast<ssize_t>(sz) - old_updates_sum + new_updates_sum;
+    INVARIANT(ret > 0);
+    return ret;
+  }
+
+  // how many bytes do we need to encode a delta record
+  static inline size_t
+  compute_needed_delta_standalone(
+      const value_type *v, uint64_t fields)
+  {
+    size_t size_needed = 0;
+    size_needed += sizeof(uint64_t);
+    if (fields == 0) {
+      // delete
+      INVARIANT(!v);
+      return size_needed;
+    }
+    INVARIANT(v);
+    if (IsAllFields(fields)) {
+      // new record (insert)
+      const value_encoder_type value_encoder;
+      size_needed += value_encoder.nbytes(v);
+      return size_needed;
+    }
+
+    for (uint64_t i = 0; i < value_descriptor_type::nfields(); i++) {
+      if ((1UL << i) & fields) {
+        const uint8_t * px = reinterpret_cast<const uint8_t *>(v) +
+          value_descriptor_type::cstruct_offsetof(i);
+        size_needed += value_descriptor_type::nbytes_fn(i)(px);
+      }
+    }
+
+    return size_needed;
+  }
+
+  static inline void
+  do_write_standalone(
+      const value_type *v, uint64_t fields,
+      uint8_t *buf, size_t sz)
+  {
+    if (fields == 0) {
+      // no-op for delete
+      INVARIANT(!v);
+      return;
+    }
+    if (IsAllFields(fields)) {
+      // special case, just use the standard encoder (faster)
+      // because it's straight-line w/ no branching
+      const value_encoder_type value_encoder;
+      value_encoder.write(buf, v);
+      return;
+    }
+    write_record_cursor<base_type> wc(buf);
+    for (uint64_t i = 0; i < value_descriptor_type::nfields(); i++) {
+      if ((1UL << i) & fields) {
+        wc.skip_to(i);
+        wc.write_current_and_advance(v, nullptr);
+      }
+    }
+  }
+
+  static inline void
+  do_delta_write_standalone(
+      const value_type *v, uint64_t fields,
+      uint8_t *buf, size_t sz)
+  {
+    serializer<uint64_t, false> s_uint64_t;
+
+#ifdef CHECK_INVARIANTS
+    const uint8_t * const orig_buf = buf;
+#endif
+
+    buf = s_uint64_t.write(buf, fields);
+    if (fields == 0) {
+      // no-op for delete
+      INVARIANT(!v);
+      return;
+    }
+    if (IsAllFields(fields)) {
+      // special case, just use the standard encoder (faster)
+      // because it's straight-line w/ no branching
+      const value_encoder_type value_encoder;
+      value_encoder.write(buf, v);
+      return;
+    }
+    for (uint64_t i = 0; i < value_descriptor_type::nfields(); i++) {
+      if ((1UL << i) & fields) {
+        const uint8_t * px = reinterpret_cast<const uint8_t *>(v) +
+          value_descriptor_type::cstruct_offsetof(i);
+        buf = value_descriptor_type::write_fn(i)(buf, px);
+      }
+    }
+
+    INVARIANT(buf - orig_buf == ptrdiff_t(sz));
+  }
+
+  template <uint64_t Fields>
+  static inline size_t
+  tuple_writer(dbtuple::TupleWriterMode mode, const void *v, uint8_t *p, size_t sz)
+  {
+    const value_type *vx = reinterpret_cast<const value_type *>(v);
+    switch (mode) {
+    case dbtuple::TUPLE_WRITER_NEEDS_OLD_VALUE:
+      return 1;
+    case dbtuple::TUPLE_WRITER_COMPUTE_NEEDED:
+      return compute_needed_standalone(vx, Fields, p, sz);
+    case dbtuple::TUPLE_WRITER_COMPUTE_DELTA_NEEDED:
+      return compute_needed_delta_standalone(vx, Fields);
+    case dbtuple::TUPLE_WRITER_DO_WRITE:
+      do_write_standalone(vx, Fields, p, sz);
+      return 0;
+    case dbtuple::TUPLE_WRITER_DO_DELTA_WRITE:
+      do_delta_write_standalone(vx, Fields, p, sz);
+      return 0;
+    }
+    ALWAYS_ASSERT(false);
+    return 0;
+  }
+
+  class value_writer {
+  public:
+    constexpr value_writer(const value_type *v, uint64_t fields)
+      : v(v), fields(fields) {}
+
+    // old version of record is stored at
+    // [buf, buf+sz).
+    //
+    // compute the new required size for the update
+    inline size_t
+    compute_needed(const uint8_t *buf, size_t sz)
+    {
+      return compute_needed_standalone(v, fields, buf, sz);
+    }
+
+    template <typename StringAllocator>
+    inline const std::string *
+    fully_materialize(bool stable_input, StringAllocator &sa)
+    {
+      INVARIANT(IsAllFields(fields) || fields == 0);
+      if (fields == 0) {
+        // delete
+        INVARIANT(!v);
+        return nullptr;
+      }
+      std::string * const ret = sa();
+      const value_encoder_type value_encoder;
+      value_encoder.write(*ret, v);
+      return ret;
+    }
+
+    // the old value lives in [buf, buf+sz), but [buf, buf+compute_needed())
+    // is valid memory to write to
+    inline void
+    operator()(uint8_t *buf, size_t sz)
+    {
+      do_write_standalone(v, fields, buf, sz);
+    }
+
+  private:
+    const value_type *v;
+    uint64_t fields;
+  };
+
+  typedef key_type Key;
+  typedef key_writer KeyWriter;
+  typedef value_type Value;
+  typedef value_writer ValueWriter;
+  typedef uint64_t ValueInfo;
+
+  //typedef key_reader KeyReader;
+  //typedef single_value_reader SingleValueReader;
+  //typedef value_reader ValueReader;
+
+};
+
+template <template <typename> class Transaction, typename Schema>
+class typed_txn_btree : public base_txn_btree<Transaction, typed_txn_btree_<Schema>> {
+  typedef base_txn_btree<Transaction, typed_txn_btree_<Schema>> super_type;
+public:
+
+  typedef typename super_type::string_type string_type;
+  typedef typename super_type::size_type size_type;
+
+  typedef typename Schema::base_type base_type;
+  typedef typename Schema::key_type key_type;
+  typedef typename Schema::value_type value_type;
+  typedef typename Schema::value_descriptor_type value_descriptor_type;
+  typedef typename Schema::key_encoder_type key_encoder_type;
+  typedef typename Schema::value_encoder_type value_encoder_type;
+
+private:
+
+  typedef txn_btree_::key_reader bytes_key_reader;
+  typedef txn_btree_::single_value_reader bytes_single_value_reader;
+  typedef txn_btree_::value_reader bytes_value_reader;
+
+  typedef
+    typename typed_txn_btree_<Schema>::key_writer
+    key_writer;
+
+  typedef
+    typename typed_txn_btree_<Schema>::key_reader
+    key_reader;
+  typedef
+    typename typed_txn_btree_<Schema>::single_value_reader
+    single_value_reader;
+  typedef
+    typename typed_txn_btree_<Schema>::value_reader
+    value_reader;
+
+  template <typename Traits>
+  static constexpr inline bool
+  IsSupportable()
+  {
+    return Traits::stable_input_memory ||
+      (private_::is_trivially_copyable<key_type>::value &&
+       private_::is_trivially_destructible<key_type>::value &&
+       private_::is_trivially_copyable<value_type>::value &&
+       private_::is_trivially_destructible<value_type>::value);
+  }
+
+public:
+
+  static const uint64_t AllFieldsMask = typed_txn_btree_<Schema>::AllFieldsMask;
+  typedef util::Fields<AllFieldsMask> AllFields;
+
+  struct search_range_callback {
+  public:
+    virtual ~search_range_callback() {}
+    virtual bool invoke(const key_type &k, const value_type &v) = 0;
+  };
+
+  struct bytes_search_range_callback {
+  public:
+    virtual ~bytes_search_range_callback() {}
+    virtual bool invoke(const string_type &k, const string_type &v) = 0;
+  };
+
+  typed_txn_btree(size_type value_size_hint = 128,
+                  bool mostly_append = false,
+                  const std::string &name = "<unknown>")
+    : super_type(value_size_hint, mostly_append, name)
+  {}
+
+  template <typename Traits, typename FieldsMask = AllFields>
+  inline bool search(
+      Transaction<Traits> &t, const key_type &k, value_type &v,
+      FieldsMask fm = FieldsMask());
+
+  template <typename Traits, typename FieldsMask = AllFields>
+  inline void search_range_call(
+      Transaction<Traits> &t, const key_type &lower, const key_type *upper,
+      search_range_callback &callback,
+      bool no_key_results = false /* skip decoding of keys? */,
+      FieldsMask fm = FieldsMask());
+
+  // a lower-level variant which does not bother to decode the key/values
+  template <typename Traits>
+  inline void bytes_search_range_call(
+      Transaction<Traits> &t, const key_type &lower, const key_type *upper,
+      bytes_search_range_callback &callback,
+      size_type value_fields_prefix = std::numeric_limits<size_type>::max());
+
+  template <typename Traits, typename FieldsMask = AllFields>
+  inline void put(
+      Transaction<Traits> &t, const key_type &k, const value_type &v,
+      FieldsMask fm = FieldsMask());
+
+  template <typename Traits>
+  inline void insert(
+      Transaction<Traits> &t, const key_type &k, const value_type &v);
+
+  template <typename Traits>
+  inline void remove(
+      Transaction<Traits> &t, const key_type &k);
+
+private:
+
+  template <typename Traits>
+  static inline const std::string *
+  stablize(Transaction<Traits> &t, const key_type &k)
+  {
+    key_writer writer(&k);
+    return writer.fully_materialize(
+        Traits::stable_input_memory, t.string_allocator());
+  }
+
+  template <typename Traits>
+  static inline const value_type *
+  stablize(Transaction<Traits> &t, const value_type &v)
+  {
+    if (Traits::stable_input_memory)
+      return &v;
+    std::string * const px = t.string_allocator()();
+    px->assign(reinterpret_cast<const char *>(&v), sizeof(v));
+    return reinterpret_cast<const value_type *>(px->data());
+  }
+
+  key_encoder_type key_encoder;
+  value_encoder_type value_encoder;
+};
+
+template <template <typename> class Transaction, typename Schema>
+template <typename Traits, typename FieldsMask>
+bool
+typed_txn_btree<Transaction, Schema>::search(
+    Transaction<Traits> &t, const key_type &k, value_type &v,
+    FieldsMask fm)
+{
+  // XXX: template single_value_reader with mask
+  single_value_reader vr(v, FieldsMask::value);
+  return this->do_search(t, k, vr);
+}
+
+template <template <typename> class Transaction, typename Schema>
+template <typename Traits, typename FieldsMask>
+void
+typed_txn_btree<Transaction, Schema>::search_range_call(
+    Transaction<Traits> &t,
+    const key_type &lower, const key_type *upper,
+    search_range_callback &callback,
+    bool no_key_results,
+    FieldsMask fm)
+{
+  key_reader kr(no_key_results);
+  value_reader vr(FieldsMask::value);
+  this->do_search_range_call(t, lower, upper, callback, kr, vr);
+}
+
+template <template <typename> class Transaction, typename Schema>
+template <typename Traits>
+void
+typed_txn_btree<Transaction, Schema>::bytes_search_range_call(
+    Transaction<Traits> &t, const key_type &lower, const key_type *upper,
+    bytes_search_range_callback &callback,
+    size_type value_fields_prefix)
+{
+  const value_encoder_type value_encoder;
+  const size_t max_bytes_read =
+    value_encoder.encode_max_nbytes_prefix(value_fields_prefix);
+  bytes_key_reader kr;
+  bytes_value_reader vr(max_bytes_read);
+  this->do_search_range_call(t, lower, upper, callback, kr, vr);
+}
+
+template <template <typename> class Transaction, typename Schema>
+template <typename Traits, typename FieldsMask>
+void
+typed_txn_btree<Transaction, Schema>::put(
+    Transaction<Traits> &t, const key_type &k, const value_type &v, FieldsMask fm)
+{
+  static_assert(IsSupportable<Traits>(), "xx");
+  const dbtuple::tuple_writer_t tw =
+    &typed_txn_btree_<Schema>::template tuple_writer<FieldsMask::value>;
+  this->do_tree_put(t, stablize(t, k), stablize(t, v), tw, false);
+}
+
+template <template <typename> class Transaction, typename Schema>
+template <typename Traits>
+void
+typed_txn_btree<Transaction, Schema>::insert(
+    Transaction<Traits> &t, const key_type &k, const value_type &v)
+{
+  static_assert(IsSupportable<Traits>(), "xx");
+  const dbtuple::tuple_writer_t tw =
+    &typed_txn_btree_<Schema>::template tuple_writer<AllFieldsMask>;
+  this->do_tree_put(t, stablize(t, k), stablize(t, v), tw, true);
+}
+
+template <template <typename> class Transaction, typename Schema>
+template <typename Traits>
+void
+typed_txn_btree<Transaction, Schema>::remove(
+    Transaction<Traits> &t, const key_type &k)
+{
+  static_assert(IsSupportable<Traits>(), "xx");
+  const dbtuple::tuple_writer_t tw =
+    &typed_txn_btree_<Schema>::template tuple_writer<0>;
+  this->do_tree_put(t, stablize(t, k), nullptr, tw, false);
+}
+
+#endif /* _NDB_TYPED_TXN_BTREE_H_ */
diff --git a/silo/util.h b/silo/util.h
new file mode 100644 (file)
index 0000000..a63f7ae
--- /dev/null
@@ -0,0 +1,712 @@
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <limits>
+#include <queue>
+#include <utility>
+#include <memory>
+#include <atomic>
+#include <tuple>
+#include <algorithm>
+
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+#include <cxxabi.h>
+
+#include "macros.h"
+#include "small_vector.h"
+
+namespace util {
+
+// padded, aligned primitives
+template <typename T, bool Pedantic = true>
+class aligned_padded_elem {
+public:
+
+  template <class... Args>
+  aligned_padded_elem(Args &&... args)
+    : elem(std::forward<Args>(args)...)
+  {
+    if (Pedantic)
+      ALWAYS_ASSERT(((uintptr_t)this % CACHELINE_SIZE) == 0);
+  }
+
+  T elem;
+  CACHE_PADOUT;
+
+  // syntactic sugar- can treat like a pointer
+  inline T & operator*() { return elem; }
+  inline const T & operator*() const { return elem; }
+  inline T * operator->() { return &elem; }
+  inline const T * operator->() const { return &elem; }
+
+private:
+  inline void
+  __cl_asserter() const
+  {
+    static_assert((sizeof(*this) % CACHELINE_SIZE) == 0, "xx");
+  }
+} CACHE_ALIGNED;
+
+// some pre-defs
+typedef aligned_padded_elem<uint8_t>  aligned_padded_u8;
+typedef aligned_padded_elem<uint16_t> aligned_padded_u16;
+typedef aligned_padded_elem<uint32_t> aligned_padded_u32;
+typedef aligned_padded_elem<uint64_t> aligned_padded_u64;
+
+template <typename T>
+struct host_endian_trfm {
+  inline ALWAYS_INLINE T operator()(const T &t) const { return t; }
+};
+
+template <>
+struct host_endian_trfm<uint16_t> {
+  inline ALWAYS_INLINE uint16_t operator()(uint16_t t) const { return be16toh(t); }
+};
+
+template <>
+struct host_endian_trfm<int16_t> {
+  inline ALWAYS_INLINE int16_t operator()(int16_t t) const { return be16toh(t); }
+};
+
+template <>
+struct host_endian_trfm<int32_t> {
+  inline ALWAYS_INLINE int32_t operator()(int32_t t) const { return be32toh(t); }
+};
+
+template <>
+struct host_endian_trfm<uint32_t> {
+  inline ALWAYS_INLINE uint32_t operator()(uint32_t t) const { return be32toh(t); }
+};
+
+template <>
+struct host_endian_trfm<int64_t> {
+  inline ALWAYS_INLINE int64_t operator()(int64_t t) const { return be64toh(t); }
+};
+
+template <>
+struct host_endian_trfm<uint64_t> {
+  inline ALWAYS_INLINE uint64_t operator()(uint64_t t) const { return be64toh(t); }
+};
+
+template <typename T>
+struct big_endian_trfm {
+  inline ALWAYS_INLINE T operator()(const T &t) const { return t; }
+};
+
+template <>
+struct big_endian_trfm<uint16_t> {
+  inline ALWAYS_INLINE uint16_t operator()(uint16_t t) const { return htobe16(t); }
+};
+
+template <>
+struct big_endian_trfm<int16_t> {
+  inline ALWAYS_INLINE int16_t operator()(int16_t t) const { return htobe16(t); }
+};
+
+template <>
+struct big_endian_trfm<int32_t> {
+  inline ALWAYS_INLINE int32_t operator()(int32_t t) const { return htobe32(t); }
+};
+
+template <>
+struct big_endian_trfm<uint32_t> {
+  inline ALWAYS_INLINE uint32_t operator()(uint32_t t) const { return htobe32(t); }
+};
+
+template <>
+struct big_endian_trfm<int64_t> {
+  inline ALWAYS_INLINE int64_t operator()(int64_t t) const { return htobe64(t); }
+};
+
+template <>
+struct big_endian_trfm<uint64_t> {
+  inline ALWAYS_INLINE uint64_t operator()(uint64_t t) const { return htobe64(t); }
+};
+
+inline std::string
+hexify_buf(const char *buf, size_t len)
+{
+  const char *const lut = "0123456789ABCDEF";
+  std::string output;
+  output.reserve(2 * len);
+  for (size_t i = 0; i < len; ++i) {
+    const unsigned char c = (unsigned char) buf[i];
+    output.push_back(lut[c >> 4]);
+    output.push_back(lut[c & 15]);
+  }
+  return output;
+}
+
+
+template <typename T>
+inline std::string
+hexify(const T &t)
+{
+  std::ostringstream buf;
+  buf << std::hex << t;
+  return buf.str();
+}
+
+template <>
+inline std::string
+hexify(const std::string &input)
+{
+  return hexify_buf(input.data(), input.size());
+}
+
+template <typename T, unsigned int lgbase>
+struct mask_ {
+  static const T value = ((T(1) << lgbase) - 1);
+};
+
+// rounding
+template <typename T, unsigned int lgbase>
+static constexpr inline ALWAYS_INLINE T
+round_up(T t)
+{
+  return (t + mask_<T, lgbase>::value) & ~mask_<T, lgbase>::value;
+}
+
+template <typename T, unsigned int lgbase>
+static constexpr inline ALWAYS_INLINE T
+round_down(T t)
+{
+  return (t & ~mask_<T, lgbase>::value);
+}
+
+template <typename T, typename U>
+static inline ALWAYS_INLINE T
+iceil(T x, U y)
+{
+  U mod = x % y;
+  return x + (mod ? y - mod : 0);
+}
+
+template <typename T>
+static inline T
+slow_round_up(T x, T q)
+{
+  const T r = x % q;
+  if (!r)
+    return x;
+  return x + (q - r);
+}
+
+template <typename T>
+static inline T
+slow_round_down(T x, T q)
+{
+  const T r = x % q;
+  if (!r)
+    return x;
+  return x - r;
+}
+
+// not thread-safe
+//
+// taken from java:
+//   http://developer.classpath.org/doc/java/util/Random-source.html
+class fast_random {
+public:
+  fast_random(unsigned long seed)
+    : seed(0)
+  {
+    set_seed0(seed);
+  }
+
+  inline unsigned long
+  next()
+  {
+    return ((unsigned long) next(32) << 32) + next(32);
+  }
+
+  inline uint32_t
+  next_u32()
+  {
+    return next(32);
+  }
+
+  inline uint16_t
+  next_u16()
+  {
+    return next(16);
+  }
+
+  /** [0.0, 1.0) */
+  inline double
+  next_uniform()
+  {
+    return (((unsigned long) next(26) << 27) + next(27)) / (double) (1L << 53);
+  }
+
+  inline char
+  next_char()
+  {
+    return next(8) % 256;
+  }
+
+  inline char
+  next_readable_char()
+  {
+    static const char readables[] = "0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
+    return readables[next(6)];
+  }
+
+  inline std::string
+  next_string(size_t len)
+  {
+    std::string s(len, 0);
+    for (size_t i = 0; i < len; i++)
+      s[i] = next_char();
+    return s;
+  }
+
+  inline std::string
+  next_readable_string(size_t len)
+  {
+    std::string s(len, 0);
+    for (size_t i = 0; i < len; i++)
+      s[i] = next_readable_char();
+    return s;
+  }
+
+  inline unsigned long
+  get_seed()
+  {
+    return seed;
+  }
+
+  inline void
+  set_seed(unsigned long seed)
+  {
+    this->seed = seed;
+  }
+
+private:
+  inline void
+  set_seed0(unsigned long seed)
+  {
+    this->seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
+  }
+
+  inline unsigned long
+  next(unsigned int bits)
+  {
+    seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
+    return (unsigned long) (seed >> (48 - bits));
+  }
+
+  unsigned long seed;
+};
+
+template <typename ForwardIterator>
+std::string
+format_list(ForwardIterator begin, ForwardIterator end)
+{
+  std::ostringstream ss;
+  ss << "[";
+  bool first = true;
+  while (begin != end) {
+    if (!first)
+      ss << ", ";
+    first = false;
+    ss << *begin++;
+  }
+  ss << "]";
+  return ss.str();
+}
+
+/**
+ * Returns the lowest position p such that p0+p != p1+p.
+ */
+inline size_t
+first_pos_diff(const char *p0, size_t sz0,
+               const char *p1, size_t sz1)
+{
+  const char *p0end = p0 + sz0;
+  const char *p1end = p1 + sz1;
+  size_t n = 0;
+  while (p0 != p0end &&
+         p1 != p1end &&
+         p0[n] == p1[n])
+    n++;
+  return n;
+}
+
+class timer {
+private:
+  timer(const timer &) = delete;
+  timer &operator=(const timer &) = delete;
+  timer(timer &&) = delete;
+
+public:
+  timer()
+  {
+    lap();
+  }
+
+  inline uint64_t
+  lap()
+  {
+    uint64_t t0 = start;
+    uint64_t t1 = cur_usec();
+    start = t1;
+    return t1 - t0;
+  }
+
+  inline double
+  lap_ms()
+  {
+    return lap() / 1000.0;
+  }
+
+  static inline uint64_t
+  cur_usec()
+  {
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return ((uint64_t)tv.tv_sec) * 1000000 + tv.tv_usec;
+  }
+
+private:
+
+  uint64_t start;
+};
+
+class scoped_timer {
+private:
+  timer t;
+  std::string region;
+  bool enabled;
+
+public:
+  scoped_timer(const std::string &region, bool enabled = true)
+    : region(region), enabled(enabled)
+  {}
+
+  ~scoped_timer()
+  {
+    if (enabled) {
+      const double x = t.lap() / 1000.0; // ms
+      std::cerr << "timed region " << region << " took " << x << " ms" << std::endl;
+    }
+  }
+};
+
+inline std::string
+next_key(const std::string &s)
+{
+  std::string s0(s);
+  s0.resize(s.size() + 1);
+  return s0;
+}
+
+template <typename T, typename Container = std::vector<T> >
+struct std_reverse_pq {
+  typedef std::priority_queue<T, Container, std::greater<T> > type;
+};
+
+template <typename PairType, typename FirstComp>
+struct std_pair_first_cmp {
+  inline bool
+  operator()(const PairType &lhs, const PairType &rhs) const
+  {
+    FirstComp c;
+    return c(lhs.first, rhs.first);
+  }
+};
+
+// deal with small container opt vectors correctly
+template <typename T, size_t SmallSize = SMALL_SIZE_VEC>
+struct vec {
+#ifdef USE_SMALL_CONTAINER_OPT
+  typedef small_vector<T, SmallSize> type;
+#else
+  typedef std::vector<T> type;
+#endif
+};
+
+static inline std::vector<std::string>
+split(const std::string &s, char delim)
+{
+  std::vector<std::string> elems;
+  std::stringstream ss(s);
+  std::string item;
+  while (std::getline(ss, item, delim))
+    elems.emplace_back(item);
+  return elems;
+}
+
+struct default_string_allocator {
+  inline std::string *
+  operator()()
+  {
+    strs.emplace_back(new std::string);
+    return strs.back().get();
+  }
+  inline void
+  return_last(std::string *px)
+  {
+    // XXX: check px in strs
+  }
+private:
+  std::vector<std::shared_ptr<std::string>> strs;
+};
+
+static constexpr uint64_t
+compute_fields_mask()
+{
+  return 0;
+}
+
+template <typename First, typename... Rest>
+static constexpr uint64_t
+compute_fields_mask(First f, Rest... rest)
+{
+  return (1UL << f) | compute_fields_mask(rest...);
+}
+
+template <uint64_t Mask>
+struct Fields {
+  static const uint64_t value = Mask;
+};
+
+#define FIELDS(args...) \
+  ::util::Fields< ::util::compute_fields_mask(args) >()
+
+#ifdef DISABLE_FIELD_SELECTION
+#define GUARDED_FIELDS(args...) \
+  ::util::Fields< ::std::numeric_limits<uint64_t>::max() >()
+#else
+#define GUARDED_FIELDS(args...) FIELDS(args)
+#endif
+
+template <typename T>
+struct cxx_typename {
+  static std::string
+  value()
+  {
+    int st;
+    char *name = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &st);
+    if (unlikely(st))
+      return std::string(typeid(T).name()) + "<demangle failed>";
+    std::string ret(name);
+    free(name);
+    return ret;
+  }
+};
+
+// returns a vector of [start, ..., end)
+template <typename T>
+static std::vector<T>
+MakeRange(T start, T end)
+{
+  std::vector<T> ret;
+  for (T i = start; i < end; i++)
+    ret.push_back(i);
+  return ret;
+}
+
+struct timespec_utils {
+       // thanks austin
+       static void
+       subtract(const struct timespec *x,
+                                        const struct timespec *y,
+                                        struct timespec *out)
+       {
+               // Perform the carry for the later subtraction by updating y.
+               struct timespec y2 = *y;
+               if (x->tv_nsec < y2.tv_nsec) {
+                       int sec = (y2.tv_nsec - x->tv_nsec) / 1e9 + 1;
+                       y2.tv_nsec -= 1e9 * sec;
+                       y2.tv_sec += sec;
+               }
+               if (x->tv_nsec - y2.tv_nsec > 1e9) {
+                       int sec = (x->tv_nsec - y2.tv_nsec) / 1e9;
+                       y2.tv_nsec += 1e9 * sec;
+                       y2.tv_sec -= sec;
+               }
+
+               // Compute the time remaining to wait.  tv_nsec is certainly
+               // positive.
+               out->tv_sec  = x->tv_sec - y2.tv_sec;
+               out->tv_nsec = x->tv_nsec - y2.tv_nsec;
+       }
+};
+
+template <typename T>
+struct RangeAwareParser {
+  inline std::vector<T>
+  operator()(const std::string &s) const
+  {
+    std::vector<T> ret;
+    if (s.find('-') == std::string::npos) {
+      T t;
+      std::istringstream iss(s);
+      iss >> t;
+      ret.emplace_back(t);
+    } else {
+      std::vector<std::string> toks(split(s, '-'));
+      ALWAYS_ASSERT(toks.size() == 2);
+      T t0, t1;
+      std::istringstream iss0(toks[0]), iss1(toks[1]);
+      iss0 >> t0;
+      iss1 >> t1;
+      for (T t = t0; t <= t1; t++)
+        ret.emplace_back(t);
+    }
+    return ret;
+  }
+};
+
+template <typename T, typename Parser>
+static std::vector<T>
+ParseCSVString(const std::string &s, Parser p = Parser())
+{
+  std::vector<T> ret;
+  std::vector<std::string> toks(split(s, ','));
+  for (auto &s : toks) {
+    auto values = p(s);
+    ret.insert(ret.end(), values.begin(), values.end());
+  }
+  return ret;
+}
+
+template <typename T>
+static inline T
+non_atomic_fetch_add(std::atomic<T> &data, T arg)
+{
+  const T ret = data.load(std::memory_order_acquire);
+  data.store(ret + arg, std::memory_order_release);
+  return ret;
+}
+
+template <typename T>
+static inline T
+non_atomic_fetch_sub(std::atomic<T> &data, T arg)
+{
+  const T ret = data.load(std::memory_order_acquire);
+  data.store(ret - arg, std::memory_order_release);
+  return ret;
+}
+
+static inline std::string
+to_lower(const std::string &s)
+{
+  std::string ret(s);
+  std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
+  return ret;
+}
+
+} // namespace util
+
+// pretty printer for std::pair<A, B>
+template <typename A, typename B>
+inline std::ostream &
+operator<<(std::ostream &o, const std::pair<A, B> &p)
+{
+  o << "[" << p.first << ", " << p.second << "]";
+  return o;
+}
+
+// pretty printer for std::vector<T, Alloc>
+template <typename T, typename Alloc>
+static std::ostream &
+operator<<(std::ostream &o, const std::vector<T, Alloc> &v)
+{
+  bool first = true;
+  o << "[";
+  for (auto &p : v) {
+    if (!first)
+      o << ", ";
+    first = false;
+    o << p;
+  }
+  o << "]";
+  return o;
+}
+
+// pretty printer for std::tuple<...>
+namespace private_ {
+  template <size_t Idx, bool Enable, class... Types>
+  struct helper {
+    static inline void
+    apply(std::ostream &o, const std::tuple<Types...> &t)
+    {
+      if (Idx)
+        o << ", ";
+      o << std::get<Idx, Types...>(t);
+      helper<Idx + 1,
+             (Idx + 1) < std::tuple_size<std::tuple<Types...>>::value,
+             Types...>::apply(o, t);
+    }
+  };
+
+  template <size_t Idx, class... Types>
+  struct helper<Idx, false, Types...> {
+    static inline void
+    apply(std::ostream &o, const std::tuple<Types...> &t)
+    {
+    }
+  };
+}
+
+template <class... Types>
+static inline std::ostream &
+operator<<(std::ostream &o, const std::tuple<Types...> &t)
+{
+  o << "[";
+  private_::helper<0, 0 < std::tuple_size<std::tuple<Types...>>::value, Types...>::apply(o, t);
+  o << "]";
+  return o;
+}
+
+// XXX: so nasty, but some things we want to explictly call their dtors we do
+// this anti-pattern all over the code base, might as well centralize it here
+template <typename T>
+class unmanaged {
+public:
+  template <class... Args>
+  unmanaged(Args &&... args)
+#ifdef CHECK_INVARIANTS
+    : destroyed_(false)
+#endif
+  {
+    new (&obj_[0]) T(std::forward<Args>(args)...);
+  }
+
+  // up to you to call this at most once
+  inline void
+  destroy()
+  {
+#ifdef CHECK_INVARIANTS
+    ALWAYS_ASSERT(!destroyed_);
+    destroyed_ = true;
+#endif
+    obj()->~T();
+  }
+
+  inline T * obj() { return (T *) &obj_[0]; }
+  inline const T * obj() const { return (const T *) &obj_[0]; }
+
+  // syntatic sugar
+
+  inline T & operator*() { return *obj(); }
+  inline const T & operator*() const { return *obj(); }
+  inline T * operator->() { return obj(); }
+  inline const T * operator->() const { return obj(); }
+
+private:
+  char obj_[sizeof(T)];
+#ifdef CHECK_INVARIANTS
+  bool destroyed_;
+#endif
+} PACKED;
+
+#endif /* _UTIL_H_ */
diff --git a/silo/varint.cc b/silo/varint.cc
new file mode 100644 (file)
index 0000000..dada3f8
--- /dev/null
@@ -0,0 +1,32 @@
+#include <iostream>
+
+#include "varint.h"
+#include "macros.h"
+#include "util.h"
+
+using namespace std;
+using namespace util;
+
+static void
+do_test(uint32_t v)
+{
+  uint8_t buf[5];
+  uint8_t *p = &buf[0];
+  p = write_uvint32(p, v);
+  ALWAYS_ASSERT(size_t(p - &buf[0]) == size_uvint32(v));
+
+  const uint8_t *p0 = &buf[0];
+  uint32_t v0 = 0;
+  p0 = read_uvint32(p0, &v0);
+  ALWAYS_ASSERT(v == v0);
+  ALWAYS_ASSERT(p == p0);
+}
+
+void
+varint::Test()
+{
+  fast_random r(2043859);
+  for (int i = 0; i < 1000; i++)
+    do_test(r.next_u32());
+  cerr << "varint tests passed" << endl;
+}
diff --git a/silo/varint.h b/silo/varint.h
new file mode 100644 (file)
index 0000000..2638d43
--- /dev/null
@@ -0,0 +1,164 @@
+#ifndef _VARINT_H_
+#define _VARINT_H_
+
+#include <stdint.h>
+#include "macros.h"
+
+// read unsigned varint32 from buffer. assumes the buffer will have enough size
+inline const uint8_t *
+read_uvint32_slow(const uint8_t *buf, uint32_t *value)
+{
+  const uint8_t *p;
+  uint32_t b, result;
+
+  p = buf;
+
+  b = *p++; result  = (b & 0x7F)      ; if (likely(b < 0x80)) goto done;
+  b = *p++; result |= (b & 0x7F) <<  7; if (likely(b < 0x80)) goto done;
+  b = *p++; result |= (b & 0x7F) << 14; if (likely(b < 0x80)) goto done;
+  b = *p++; result |= (b & 0x7F) << 21; if (likely(b < 0x80)) goto done;
+  b = *p++; result |=  b         << 28; if (likely(b < 0x80)) goto done;
+
+  ALWAYS_ASSERT(false); // should not reach here (improper encoding)
+
+done:
+  *value = result;
+  return p;
+}
+
+/**
+ * Read a uvint32 from buf into value, returning the
+ * next position in buf after the value has been read.
+ *
+ * Assumes buf points to a well encoded varint
+ */
+inline ALWAYS_INLINE const uint8_t *
+read_uvint32(const uint8_t *buf, uint32_t *value)
+{
+  if (likely(*buf < 0x80)) {
+    *value = *buf;
+    return buf + 1;
+  }
+  return read_uvint32_slow(buf, value);
+}
+
+inline const uint8_t *
+failsafe_read_uvint32_slow(
+    const uint8_t *buf, size_t nbytes, uint32_t *value)
+{
+  const uint8_t *p;
+  uint32_t b, result;
+
+  p = buf;
+
+  if (unlikely(!nbytes--)) return nullptr;
+  b = *p++; result  = (b & 0x7F)      ; if (likely(b < 0x80)) goto done;
+  if (unlikely(!nbytes--)) return nullptr;
+  b = *p++; result |= (b & 0x7F) <<  7; if (likely(b < 0x80)) goto done;
+  if (unlikely(!nbytes--)) return nullptr;
+  b = *p++; result |= (b & 0x7F) << 14; if (likely(b < 0x80)) goto done;
+  if (unlikely(!nbytes--)) return nullptr;
+  b = *p++; result |= (b & 0x7F) << 21; if (likely(b < 0x80)) goto done;
+  if (unlikely(!nbytes--)) return nullptr;
+  b = *p++; result |=  b         << 28; if (likely(b < 0x80)) goto done;
+
+done:
+  *value = result;
+  return p;
+}
+
+inline ALWAYS_INLINE const uint8_t *
+failsafe_read_uvint32(
+    const uint8_t *stream, size_t nbytes, uint32_t *value)
+{
+  if (unlikely(!nbytes))
+    return nullptr;
+  const uint8_t ch = *stream;
+  if (likely(ch < 0x80)) {
+    *value = ch;
+    return stream + 1;
+  }
+  return failsafe_read_uvint32_slow(stream, nbytes, value);
+}
+
+inline ALWAYS_INLINE size_t
+skip_uvint32(const uint8_t *stream, uint8_t *rawv)
+{
+  if (rawv) {
+    if (likely((rawv[0] = stream[0]) < 0x80)) return 1;
+    if (likely((rawv[1] = stream[1]) < 0x80)) return 2;
+    if (likely((rawv[2] = stream[2]) < 0x80)) return 3;
+    if (likely((rawv[3] = stream[3]) < 0x80)) return 4;
+    if (likely((rawv[4] = stream[4]) < 0x80)) return 5;
+  } else {
+    if (likely(stream[0] < 0x80)) return 1;
+    if (likely(stream[1] < 0x80)) return 2;
+    if (likely(stream[2] < 0x80)) return 3;
+    if (likely(stream[3] < 0x80)) return 4;
+    if (likely(stream[4] < 0x80)) return 5;
+  }
+  ALWAYS_ASSERT(false);
+  return 0;
+}
+
+inline ALWAYS_INLINE size_t
+failsafe_skip_uvint32(const uint8_t *stream, size_t nbytes, uint8_t *rawv)
+{
+  if (rawv) {
+    if (unlikely(!nbytes--)) return 0;
+    if (likely((rawv[0] = stream[0]) < 0x80)) return 1;
+    if (unlikely(!nbytes--)) return 0;
+    if (likely((rawv[1] = stream[1]) < 0x80)) return 2;
+    if (unlikely(!nbytes--)) return 0;
+    if (likely((rawv[2] = stream[2]) < 0x80)) return 3;
+    if (unlikely(!nbytes--)) return 0;
+    if (likely((rawv[3] = stream[3]) < 0x80)) return 4;
+    if (unlikely(!nbytes--)) return 0;
+    if (likely((rawv[4] = stream[4]) < 0x80)) return 5;
+  } else {
+    if (unlikely(!nbytes--)) return 0;
+    if (likely(stream[0] < 0x80)) return 1;
+    if (unlikely(!nbytes--)) return 0;
+    if (likely(stream[1] < 0x80)) return 2;
+    if (unlikely(!nbytes--)) return 0;
+    if (likely(stream[2] < 0x80)) return 3;
+    if (unlikely(!nbytes--)) return 0;
+    if (likely(stream[3] < 0x80)) return 4;
+    if (unlikely(!nbytes--)) return 0;
+    if (likely(stream[4] < 0x80)) return 5;
+  }
+  ALWAYS_ASSERT(false);
+  return 0;
+}
+
+/**
+ * write uint32_t as unsigned varint32 to buffer. assumes the buffer will have
+ * enough size. returns the position in buf after the value has been written
+ */
+inline uint8_t *
+write_uvint32(uint8_t *buf, uint32_t value)
+{
+  while (value > 0x7F) {
+    *buf++ = (((uint8_t) value) & 0x7F) | 0x80;
+    value >>= 7;
+  }
+  *buf++ = ((uint8_t) value) & 0x7F;
+  return buf;
+}
+
+inline size_t
+size_uvint32(uint32_t value)
+{
+  if (likely(value <= 0x7F))                                               return 1;
+  if (likely(value <= ((0x7F << 7) | 0x7F)))                               return 2;
+  if (likely(value <= ((0x7F << 14) | (0x7F << 7) | 0x7F)))                return 3;
+  if (likely(value <= ((0x7F << 21) | (0x7F << 14) | (0x7F << 7) | 0x7F))) return 4;
+  return 5;
+}
+
+class varint {
+public:
+  static void Test();
+};
+
+#endif /* _VARINT_H_ */
diff --git a/silo/varkey.h b/silo/varkey.h
new file mode 100644 (file)
index 0000000..1df909e
--- /dev/null
@@ -0,0 +1,222 @@
+#ifndef _NDB_VARKEY_H_
+#define _NDB_VARKEY_H_
+
+#include <endian.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <iostream>
+#include <string>
+#include <type_traits>
+#include <limits>
+
+#include "imstring.h"
+#include "macros.h"
+#include "util.h"
+
+#if NDB_MASSTREE
+#include "prefetch.h"
+#include "masstree/config.h"
+#include "masstree/string_slice.hh"
+#endif
+
+class varkey {
+  friend std::ostream &operator<<(std::ostream &o, const varkey &k);
+public:
+  inline varkey() : p(NULL), l(0) {}
+  inline varkey(const varkey &that) = default;
+  inline varkey(varkey &&that) = default;
+  inline varkey &operator=(const varkey &that) = default;
+
+  inline varkey(const uint8_t *p, size_t l)
+    : p(p), l(l)
+  {
+  }
+
+  explicit inline varkey(const std::string &s)
+    : p((const uint8_t *) s.data()), l(s.size())
+  {
+  }
+
+  explicit inline varkey(const char *s)
+    : p((const uint8_t *) s), l(strlen(s))
+  {
+  }
+
+  explicit inline varkey(const imstring &s)
+    : p(s.data()), l(s.size())
+  {
+  }
+
+  inline bool
+  operator==(const varkey &that) const
+  {
+    if (size() != that.size())
+      return false;
+    return memcmp(data(), that.data(), size()) == 0;
+  }
+
+  inline bool
+  operator!=(const varkey &that) const
+  {
+    return !operator==(that);
+  }
+
+  inline bool
+  operator<(const varkey &that) const
+  {
+    int r = memcmp(data(), that.data(), std::min(size(), that.size()));
+    return r < 0 || (r == 0 && size() < that.size());
+  }
+
+  inline bool
+  operator>=(const varkey &that) const
+  {
+    return !operator<(that);
+  }
+
+  inline bool
+  operator<=(const varkey &that) const
+  {
+    int r = memcmp(data(), that.data(), std::min(size(), that.size()));
+    return r < 0 || (r == 0 && size() <= that.size());
+  }
+
+  inline bool
+  operator>(const varkey &that) const
+  {
+    return !operator<=(that);
+  }
+
+  inline uint64_t
+  slice() const
+  {
+    uint64_t ret = 0;
+    uint8_t *rp = (uint8_t *) &ret;
+    for (size_t i = 0; i < std::min(l, size_t(8)); i++)
+      rp[i] = p[i];
+    return util::host_endian_trfm<uint64_t>()(ret);
+  }
+
+#if NDB_MASSTREE
+  inline uint64_t slice_at(int pos) const {
+    return string_slice<uint64_t>::make_comparable((const char*) p + pos, std::min(int(l - pos), 8));
+  }
+#endif
+
+  inline varkey
+  shift() const
+  {
+    INVARIANT(l >= 8);
+    return varkey(p + 8, l - 8);
+  }
+
+  inline varkey
+  shift_many(size_t n) const
+  {
+    INVARIANT(l >= 8 * n);
+    return varkey(p + 8 * n, l - 8 * n);
+  }
+
+  inline size_t
+  size() const
+  {
+    return l;
+  }
+
+  inline int length() const {
+    return l;
+  }
+
+  inline const uint8_t *
+  data() const
+  {
+    return p;
+  }
+
+  inline
+  std::string str() const
+  {
+    return std::string((const char *) p, l);
+  }
+
+  inline std::string &
+  str(std::string &buf) const
+  {
+    buf.assign((const char *) p, l);
+    return buf;
+  }
+
+#if NDB_MASSTREE
+  inline operator lcdf::Str() const {
+    return lcdf::Str(p, l);
+  }
+#endif
+
+private:
+  const uint8_t *p;
+  size_t l;
+};
+
+inline std::ostream &
+operator<<(std::ostream &o, const varkey &k)
+{
+  o << util::hexify(k.str());
+  return o;
+}
+
+template <bool is_signed, typename T>
+struct signed_aware_trfm {};
+
+template <typename T>
+struct signed_aware_trfm<false, T> {
+  inline ALWAYS_INLINE T operator()(T t) const { return t; }
+};
+
+template <typename T>
+struct signed_aware_trfm<true, T> {
+  typedef T signed_type;
+  typedef
+    typename std::enable_if<std::is_signed<T>::value, typename std::make_unsigned<T>::type>::type
+    unsigned_type;
+  inline ALWAYS_INLINE unsigned_type
+  operator()(signed_type s) const
+  {
+    const unsigned_type offset = -std::numeric_limits<signed_type>::min();
+    const unsigned_type converted = static_cast<unsigned_type>(s) + offset;
+    return converted;
+  }
+};
+
+template <typename T,
+          typename EncodingTrfm = signed_aware_trfm<std::is_signed<T>::value, T>,
+          typename ByteTrfm = util::big_endian_trfm<T> >
+class obj_varkey : public varkey {
+public:
+
+  typedef
+    typename std::enable_if<std::is_integral<T>::value, T>::type
+    integral_type;
+
+  inline obj_varkey() : varkey(), obj() {}
+
+  inline obj_varkey(integral_type t)
+    : varkey((const uint8_t *) &obj, sizeof(integral_type)),
+      obj(ByteTrfm()(EncodingTrfm()(t)))
+  {
+  }
+
+private:
+  integral_type obj;
+};
+
+typedef obj_varkey<uint8_t>  u8_varkey;
+typedef obj_varkey<int8_t>   s8_varkey;
+typedef obj_varkey<uint16_t> u16_varkey;
+typedef obj_varkey<int16_t>  s16_varkey;
+typedef obj_varkey<uint32_t> u32_varkey;
+typedef obj_varkey<int32_t>  s32_varkey;
+typedef obj_varkey<uint64_t> u64_varkey;
+typedef obj_varkey<int64_t>  s64_varkey;
+
+#endif /* _NDB_VARKEY_H_ */