Compare commits
38 Commits
cf-integri
...
ceecc62a63
| Author | SHA1 | Date | |
|---|---|---|---|
| ceecc62a63 | |||
| 80f0697e79 | |||
| ce23d3995c | |||
| 6f899e063b | |||
| e5b9c03e77 | |||
| a158d375f5 | |||
| ee5a84cd7b | |||
| 33f14e3d9b | |||
| 77262ee2d3 | |||
| 9945998e05 | |||
| 2777e016ff | |||
| 661ffcd843 | |||
| 3a34d3cecb | |||
| 189c73e3bd | |||
| 35987030fc | |||
| 0621741ec3 | |||
| f5ec9f726a | |||
| 552fc11c5d | |||
| 71ace9cc55 | |||
| bcf459304f | |||
| f403c78410 | |||
| 08958d4109 | |||
| dcc5275ec9 | |||
| c5ef843f9e | |||
| b78e817e24 | |||
| 9c82f17e20 | |||
| 665a9313a4 | |||
| 6e66202d5e | |||
| a92271a205 | |||
| 0dbfb4deae | |||
| 6e229b6b36 | |||
| 2200de11c8 | |||
| b37feb58dd | |||
| 94a4802824 | |||
| 707dbdb391 | |||
| bdd343bb57 | |||
| 7b31bd5efe | |||
| e255e1a926 |
@@ -57,6 +57,7 @@ ConflictSet::ReadRange prefixRange(Arena &arena, TrivialSpan key) {
|
||||
|
||||
void benchConflictSet() {
|
||||
ankerl::nanobench::Bench bench;
|
||||
bench.minEpochIterations(10000);
|
||||
ConflictSet cs{0};
|
||||
|
||||
bench.batch(kOpsPerTx);
|
||||
|
||||
@@ -31,11 +31,24 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
"MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
add_compile_options(-fdata-sections -ffunction-sections -Wswitch-enum
|
||||
-Werror=switch-enum -fPIC)
|
||||
add_compile_options(
|
||||
-Werror=switch-enum
|
||||
-Wswitch-enum
|
||||
-Wunused-variable
|
||||
-fPIC
|
||||
-fdata-sections
|
||||
-ffunction-sections
|
||||
-fno-jump-tables # https://github.com/llvm/llvm-project/issues/54247
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
add_link_options("-Wno-unused-command-line-argument")
|
||||
find_program(LLVM_OBJCOPY llvm-objcopy)
|
||||
if(LLVM_OBJCOPY)
|
||||
set(CMAKE_OBJCOPY
|
||||
${LLVM_OBJCOPY}
|
||||
CACHE FILEPATH "path to objcopy binary" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
@@ -56,6 +69,22 @@ if(HAS_FULL_RELRO)
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL
|
||||
arm64)
|
||||
add_compile_options(-mbranch-protection=standard)
|
||||
else()
|
||||
add_compile_options(-fcf-protection)
|
||||
set(rewrite_endbr_flags "-fuse-ld=mold;LINKER:-z,rewrite-endbr")
|
||||
cmake_push_check_state()
|
||||
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${rewrite_endbr_flags})
|
||||
check_cxx_source_compiles("int main(){}" HAS_REWRITE_ENDBR FAIL_REGEX
|
||||
"warning:")
|
||||
if(HAS_REWRITE_ENDBR)
|
||||
add_link_options(${rewrite_endbr_flags})
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
endif()
|
||||
|
||||
set(version_script_flags
|
||||
LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.map)
|
||||
cmake_push_check_state()
|
||||
@@ -81,19 +110,11 @@ else()
|
||||
add_link_options(-Wl,--gc-sections)
|
||||
endif()
|
||||
|
||||
if(NOT USE_SIMD_FALLBACK)
|
||||
cmake_push_check_state()
|
||||
list(APPEND CMAKE_REQUIRED_FLAGS -mavx)
|
||||
check_include_file_cxx("immintrin.h" HAS_AVX)
|
||||
if(HAS_AVX)
|
||||
if(USE_SIMD_FALLBACK)
|
||||
add_compile_definitions(USE_SIMD_FALLBACK)
|
||||
else()
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64)
|
||||
add_compile_options(-mavx)
|
||||
add_compile_definitions(HAS_AVX)
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
|
||||
check_include_file_cxx("arm_neon.h" HAS_ARM_NEON)
|
||||
if(HAS_ARM_NEON)
|
||||
add_compile_definitions(HAS_ARM_NEON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -356,6 +377,15 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
||||
${symbol_imports})
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
find_program(HARDENING_CHECK hardening-check)
|
||||
if(HARDENING_CHECK)
|
||||
add_test(NAME hardening_check
|
||||
COMMAND ${HARDENING_CHECK} $<TARGET_FILE:${PROJECT_NAME}>
|
||||
--nofortify --nostackprotector)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# bench
|
||||
add_executable(conflict_set_bench Bench.cpp)
|
||||
target_link_libraries(conflict_set_bench PRIVATE ${PROJECT_NAME} nanobench)
|
||||
|
||||
316
ConflictSet.cpp
316
ConflictSet.cpp
@@ -14,6 +14,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#if !defined(USE_SIMD_FALLBACK) && defined(__has_include)
|
||||
#if defined(__x86_64__) && __has_include("immintrin.h")
|
||||
#define HAS_AVX 1
|
||||
#include <immintrin.h>
|
||||
#elif __has_include("arm_neon.h")
|
||||
#define HAS_ARM_NEON 1
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "ConflictSet.h"
|
||||
#include "Internal.h"
|
||||
#include "LongestCommonPrefix.h"
|
||||
@@ -34,12 +44,6 @@ limitations under the License.
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef HAS_AVX
|
||||
#include <immintrin.h>
|
||||
#elif defined(HAS_ARM_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#ifndef __SANITIZE_THREAD__
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(thread_sanitizer)
|
||||
@@ -341,8 +345,8 @@ struct Node3 : Node {
|
||||
// Sorted
|
||||
uint8_t index[kMaxNodes];
|
||||
|
||||
TaggedNodePointer children[kMaxNodes];
|
||||
InternalVersionT childMaxVersion[kMaxNodes];
|
||||
TaggedNodePointer children[kMaxNodes];
|
||||
|
||||
uint8_t *partialKey() {
|
||||
assert(!releaseDeferred);
|
||||
@@ -690,14 +694,17 @@ constexpr int getMaxCapacity(Node *self) {
|
||||
self->partialKeyLen);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Disabling the free list altogether is faster on my mac m1
|
||||
constexpr int64_t kMaxFreeListBytes = 0;
|
||||
#else
|
||||
constexpr int64_t kMaxFreeListBytes = 1 << 20;
|
||||
#endif
|
||||
|
||||
// Maintains a free list up to kMaxFreeListBytes. If the top element of the list
|
||||
// doesn't meet the capacity constraints, it's freed and a new node is allocated
|
||||
// with the minimum capacity. The hope is that "unfit" nodes don't get stuck in
|
||||
// the free list.
|
||||
//
|
||||
// TODO valgrind annotations
|
||||
template <class T> struct NodeAllocator {
|
||||
|
||||
static_assert(std::derived_from<T, Node>);
|
||||
@@ -727,6 +734,8 @@ template <class T> struct NodeAllocator {
|
||||
}
|
||||
|
||||
void release(T *p) {
|
||||
assume(p->partialKeyCapacity >= 0);
|
||||
assume(freeListSize >= 0);
|
||||
if (freeListSize + sizeof(T) + p->partialKeyCapacity > kMaxFreeListBytes) {
|
||||
removeNode(p);
|
||||
return safe_free(p, sizeof(T) + p->partialKeyCapacity);
|
||||
@@ -734,6 +743,7 @@ template <class T> struct NodeAllocator {
|
||||
p->parent = freeList;
|
||||
freeList = p;
|
||||
freeListSize += sizeof(T) + p->partialKeyCapacity;
|
||||
VALGRIND_MAKE_MEM_NOACCESS(p, sizeof(T) + p->partialKeyCapacity);
|
||||
}
|
||||
|
||||
void deferRelease(T *p, Node *forwardTo) {
|
||||
@@ -755,6 +765,13 @@ template <class T> struct NodeAllocator {
|
||||
void releaseDeferred() {
|
||||
if (deferredList != nullptr) {
|
||||
deferredListFront->parent = freeList;
|
||||
#ifndef NVALGRIND
|
||||
for (auto *iter = deferredList; iter != freeList;) {
|
||||
auto *tmp = iter;
|
||||
iter = (T *)iter->parent;
|
||||
VALGRIND_MAKE_MEM_NOACCESS(tmp, sizeof(T) + tmp->partialKeyCapacity);
|
||||
}
|
||||
#endif
|
||||
freeList = std::exchange(deferredList, nullptr);
|
||||
}
|
||||
for (T *n = std::exchange(deferredListOverflow, nullptr); n != nullptr;) {
|
||||
@@ -775,6 +792,7 @@ template <class T> struct NodeAllocator {
|
||||
assert(deferredList == nullptr);
|
||||
assert(deferredListOverflow == nullptr);
|
||||
for (T *iter = freeList; iter != nullptr;) {
|
||||
VALGRIND_MAKE_MEM_DEFINED(iter, sizeof(T));
|
||||
auto *tmp = iter;
|
||||
iter = (T *)iter->parent;
|
||||
removeNode(tmp);
|
||||
@@ -792,6 +810,7 @@ private:
|
||||
|
||||
T *allocate_helper(int minCapacity, int maxCapacity) {
|
||||
if (freeList != nullptr) {
|
||||
VALGRIND_MAKE_MEM_DEFINED(freeList, sizeof(T));
|
||||
freeListSize -= sizeof(T) + freeList->partialKeyCapacity;
|
||||
assume(freeList->partialKeyCapacity >= 0);
|
||||
assume(minCapacity >= 0);
|
||||
@@ -800,6 +819,11 @@ private:
|
||||
freeList->partialKeyCapacity <= maxCapacity) {
|
||||
auto *result = freeList;
|
||||
freeList = (T *)freeList->parent;
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(result,
|
||||
sizeof(T) + result->partialKeyCapacity);
|
||||
VALGRIND_MAKE_MEM_DEFINED(&result->partialKeyCapacity,
|
||||
sizeof(result->partialKeyCapacity));
|
||||
VALGRIND_MAKE_MEM_DEFINED(&result->type, sizeof(result->type));
|
||||
return result;
|
||||
} else {
|
||||
auto *p = freeList;
|
||||
@@ -943,8 +967,7 @@ private:
|
||||
NodeAllocator<Node256> node256;
|
||||
};
|
||||
|
||||
int getNodeIndex(Node3 *self, uint8_t index) {
|
||||
Node3 *n = (Node3 *)self;
|
||||
int getNodeIndex(Node3 *n, uint8_t index) {
|
||||
assume(n->numChildren >= 1);
|
||||
assume(n->numChildren <= 3);
|
||||
for (int i = 0; i < n->numChildren; ++i) {
|
||||
@@ -1257,33 +1280,32 @@ TaggedNodePointer getChild(Node *self, uint8_t index) {
|
||||
struct ChildAndMaxVersion {
|
||||
TaggedNodePointer child;
|
||||
InternalVersionT maxVersion;
|
||||
static ChildAndMaxVersion empty() {
|
||||
ChildAndMaxVersion result;
|
||||
result.child = nullptr;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
ChildAndMaxVersion getChildAndMaxVersion(Node0 *, uint8_t) { return {}; }
|
||||
ChildAndMaxVersion getChildAndMaxVersion(Node3 *self, uint8_t index) {
|
||||
int i = getNodeIndex(self, index);
|
||||
if (i < 0) {
|
||||
ChildAndMaxVersion result;
|
||||
result.child = nullptr;
|
||||
return result;
|
||||
return ChildAndMaxVersion::empty();
|
||||
}
|
||||
return {self->children[i], self->childMaxVersion[i]};
|
||||
}
|
||||
ChildAndMaxVersion getChildAndMaxVersion(Node16 *self, uint8_t index) {
|
||||
int i = getNodeIndex(self, index);
|
||||
if (i < 0) {
|
||||
ChildAndMaxVersion result;
|
||||
result.child = nullptr;
|
||||
return result;
|
||||
return ChildAndMaxVersion::empty();
|
||||
}
|
||||
return {self->children[i], self->childMaxVersion[i]};
|
||||
}
|
||||
ChildAndMaxVersion getChildAndMaxVersion(Node48 *self, uint8_t index) {
|
||||
int i = self->index[index];
|
||||
if (i < 0) {
|
||||
ChildAndMaxVersion result;
|
||||
result.child = nullptr;
|
||||
return result;
|
||||
return ChildAndMaxVersion::empty();
|
||||
}
|
||||
return {self->children[i], self->childMaxVersion[i]};
|
||||
}
|
||||
@@ -1366,17 +1388,11 @@ TaggedNodePointer getChildGeq(Node16 *self, int child) {
|
||||
|
||||
TaggedNodePointer getChildGeq(Node48 *self, int child) {
|
||||
int c = self->bitSet.firstSetGeq(child);
|
||||
if (c < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return self->children[self->index[c]];
|
||||
return c < 0 ? nullptr : self->children[self->index[c]];
|
||||
}
|
||||
TaggedNodePointer getChildGeq(Node256 *self, int child) {
|
||||
int c = self->bitSet.firstSetGeq(child);
|
||||
if (c < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return self->children[c];
|
||||
return c < 0 ? nullptr : self->children[c];
|
||||
}
|
||||
|
||||
TaggedNodePointer getChildGeq(Node *self, int child) {
|
||||
@@ -1396,22 +1412,25 @@ TaggedNodePointer getChildGeq(Node *self, int child) {
|
||||
}
|
||||
}
|
||||
|
||||
Node *getFirstChild(Node0 *) { return nullptr; }
|
||||
Node *getFirstChild(Node3 *self) {
|
||||
return self->numChildren == 0 ? nullptr : self->children[0];
|
||||
TaggedNodePointer getFirstChild(Node0 *) { return nullptr; }
|
||||
TaggedNodePointer getFirstChild(Node3 *self) {
|
||||
// Improves scan performance
|
||||
__builtin_prefetch(self->children[1]);
|
||||
return self->children[0];
|
||||
}
|
||||
Node *getFirstChild(Node16 *self) {
|
||||
return self->numChildren == 0 ? nullptr : self->children[0];
|
||||
TaggedNodePointer getFirstChild(Node16 *self) {
|
||||
// Improves scan performance
|
||||
__builtin_prefetch(self->children[1]);
|
||||
return self->children[0];
|
||||
}
|
||||
Node *getFirstChild(Node48 *self) {
|
||||
int index = self->index[self->bitSet.firstSetGeq(0)];
|
||||
return index < 0 ? nullptr : self->children[index];
|
||||
TaggedNodePointer getFirstChild(Node48 *self) {
|
||||
return self->children[self->index[self->bitSet.firstSetGeq(0)]];
|
||||
}
|
||||
Node *getFirstChild(Node256 *self) {
|
||||
TaggedNodePointer getFirstChild(Node256 *self) {
|
||||
return self->children[self->bitSet.firstSetGeq(0)];
|
||||
}
|
||||
|
||||
Node *getFirstChild(Node *self) {
|
||||
TaggedNodePointer getFirstChild(Node *self) {
|
||||
// Only require that the node-specific overloads are covered
|
||||
// GCOVR_EXCL_START
|
||||
switch (self->getType()) {
|
||||
@@ -1431,46 +1450,6 @@ Node *getFirstChild(Node *self) {
|
||||
// GCOVR_EXCL_STOP
|
||||
}
|
||||
|
||||
// Precondition: self has a child
|
||||
TaggedNodePointer getFirstChildExists(Node3 *self) {
|
||||
assert(self->numChildren > 0);
|
||||
return self->children[0];
|
||||
}
|
||||
// Precondition: self has a child
|
||||
TaggedNodePointer getFirstChildExists(Node16 *self) {
|
||||
assert(self->numChildren > 0);
|
||||
return self->children[0];
|
||||
}
|
||||
// Precondition: self has a child
|
||||
TaggedNodePointer getFirstChildExists(Node48 *self) {
|
||||
return self->children[self->index[self->bitSet.firstSetGeq(0)]];
|
||||
}
|
||||
// Precondition: self has a child
|
||||
TaggedNodePointer getFirstChildExists(Node256 *self) {
|
||||
return self->children[self->bitSet.firstSetGeq(0)];
|
||||
}
|
||||
|
||||
// Precondition: self has a child
|
||||
TaggedNodePointer getFirstChildExists(Node *self) {
|
||||
// Only require that the node-specific overloads are covered
|
||||
// GCOVR_EXCL_START
|
||||
switch (self->getType()) {
|
||||
case Type_Node0:
|
||||
__builtin_unreachable();
|
||||
case Type_Node3:
|
||||
return getFirstChildExists(static_cast<Node3 *>(self));
|
||||
case Type_Node16:
|
||||
return getFirstChildExists(static_cast<Node16 *>(self));
|
||||
case Type_Node48:
|
||||
return getFirstChildExists(static_cast<Node48 *>(self));
|
||||
case Type_Node256:
|
||||
return getFirstChildExists(static_cast<Node256 *>(self));
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
// GCOVR_EXCL_STOP
|
||||
}
|
||||
|
||||
// self must not be the root
|
||||
void maybeDecreaseCapacity(Node *&self, WriteContext *writeContext,
|
||||
ConflictSet::Impl *impl);
|
||||
@@ -1724,7 +1703,7 @@ TaggedNodePointer &getOrCreateChild(TaggedNodePointer &self, TrivialSpan &key,
|
||||
}
|
||||
|
||||
Node *nextPhysical(Node *node) {
|
||||
auto nextChild = getFirstChild(node);
|
||||
Node *nextChild = getFirstChild(node);
|
||||
if (nextChild != nullptr) {
|
||||
return nextChild;
|
||||
}
|
||||
@@ -1742,7 +1721,7 @@ Node *nextPhysical(Node *node) {
|
||||
}
|
||||
|
||||
Node *nextLogical(Node *node) {
|
||||
auto nextChild = getFirstChild(node);
|
||||
Node *nextChild = getFirstChild(node);
|
||||
if (nextChild != nullptr) {
|
||||
node = nextChild;
|
||||
goto downLeftSpine;
|
||||
@@ -1760,7 +1739,7 @@ Node *nextLogical(Node *node) {
|
||||
}
|
||||
}
|
||||
downLeftSpine:
|
||||
for (; !node->entryPresent; node = getFirstChildExists(node)) {
|
||||
for (; !node->entryPresent; node = getFirstChild(node)) {
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1930,7 +1909,7 @@ void mergeWithChild(TaggedNodePointer &self, WriteContext *writeContext,
|
||||
child->parentsIndex = self->parentsIndex;
|
||||
|
||||
// Max versions are stored in the parent, so we need to update it now
|
||||
// that we have a new parent. Safe we call since the root never has a partial
|
||||
// that we have a new parent. Safe to call since the root never has a partial
|
||||
// key.
|
||||
setMaxVersion(child, std::max(childMaxVersion, writeContext->zero));
|
||||
|
||||
@@ -2808,7 +2787,7 @@ bool checkRangeStartsWith(NodeT *nTyped, TrivialSpan key, int begin, int end,
|
||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||
|
||||
downLeftSpine:
|
||||
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||
for (; !n->entryPresent; n = getFirstChild(n)) {
|
||||
}
|
||||
return n->entry.rangeVersion <= readVersion;
|
||||
}
|
||||
@@ -3183,6 +3162,12 @@ Node *firstGeqPhysical(Node *n, const TrivialSpan key) {
|
||||
#define PRESERVE_NONE
|
||||
#endif
|
||||
|
||||
#if __has_attribute(musttail) && __has_attribute(preserve_none)
|
||||
constexpr bool kEnableInterleaved = true;
|
||||
#else
|
||||
constexpr bool kEnableInterleaved = false;
|
||||
#endif
|
||||
|
||||
namespace check {
|
||||
|
||||
typedef PRESERVE_NONE void (*Continuation)(struct Job *, struct Context *);
|
||||
@@ -3259,7 +3244,7 @@ PRESERVE_NONE void down_left_spine(Job *job, Context *context) {
|
||||
job->setResult(n->entry.rangeVersion <= job->readVersion);
|
||||
MUSTTAIL return complete(job, context);
|
||||
}
|
||||
auto child = getFirstChildExists(n);
|
||||
auto child = getFirstChild(n);
|
||||
job->n = child;
|
||||
__builtin_prefetch(job->n);
|
||||
job->continuation = downLeftSpineTable[child.getType()];
|
||||
@@ -3354,7 +3339,7 @@ template <class NodeT> void iter(Job *job, Context *context) {
|
||||
job->setResult(n->entry.pointVersion <= job->readVersion);
|
||||
MUSTTAIL return complete(job, context);
|
||||
}
|
||||
auto c = getFirstChildExists(n);
|
||||
auto c = getFirstChild(n);
|
||||
job->n = c;
|
||||
job->continuation = downLeftSpineTable[c.getType()];
|
||||
__builtin_prefetch(job->n);
|
||||
@@ -3878,7 +3863,7 @@ void left_side_down_left_spine(Job *job, Context *context) {
|
||||
}
|
||||
MUSTTAIL return done_left_side_iter(job, context);
|
||||
}
|
||||
auto c = getFirstChildExists(n);
|
||||
auto c = getFirstChild(n);
|
||||
job->n = c;
|
||||
job->continuation = leftSideDownLeftSpineTable[c.getType()];
|
||||
__builtin_prefetch(job->n);
|
||||
@@ -4541,7 +4526,7 @@ bool checkPointRead(Node *n, const TrivialSpan key,
|
||||
if (n->entryPresent) {
|
||||
return n->entry.pointVersion <= readVersion;
|
||||
}
|
||||
n = getFirstChildExists(n);
|
||||
n = getFirstChild(n);
|
||||
goto downLeftSpine;
|
||||
}
|
||||
|
||||
@@ -4595,7 +4580,7 @@ bool checkPointRead(Node *n, const TrivialSpan key,
|
||||
}
|
||||
}
|
||||
downLeftSpine:
|
||||
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||
for (; !n->entryPresent; n = getFirstChild(n)) {
|
||||
}
|
||||
return n->entry.rangeVersion <= readVersion;
|
||||
}
|
||||
@@ -4670,7 +4655,7 @@ bool checkPrefixRead(Node *n, const TrivialSpan key,
|
||||
}
|
||||
}
|
||||
downLeftSpine:
|
||||
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||
for (; !n->entryPresent; n = getFirstChild(n)) {
|
||||
}
|
||||
return n->entry.rangeVersion <= readVersion;
|
||||
}
|
||||
@@ -4757,7 +4742,7 @@ bool checkRangeLeftSide(Node *n, TrivialSpan key, int prefixLen,
|
||||
}
|
||||
}
|
||||
downLeftSpine:
|
||||
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||
for (; !n->entryPresent; n = getFirstChild(n)) {
|
||||
}
|
||||
return n->entry.rangeVersion <= readVersion;
|
||||
}
|
||||
@@ -4846,14 +4831,12 @@ backtrack:
|
||||
searchPathLen -= 1 + n->partialKeyLen;
|
||||
n = n->parent;
|
||||
} else {
|
||||
searchPathLen -= n->partialKeyLen;
|
||||
n = next;
|
||||
searchPathLen += n->partialKeyLen;
|
||||
goto downLeftSpine;
|
||||
}
|
||||
}
|
||||
downLeftSpine:
|
||||
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||
for (; !n->entryPresent; n = getFirstChild(n)) {
|
||||
}
|
||||
return n->entry.rangeVersion <= readVersion;
|
||||
}
|
||||
@@ -4969,51 +4952,50 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
check::Context context;
|
||||
context.readContext.impl = this;
|
||||
|
||||
#if __has_attribute(musttail)
|
||||
if (count == 1) {
|
||||
useSequential(reads, result, count, context);
|
||||
} else {
|
||||
constexpr int kConcurrent = 16;
|
||||
check::Job inProgress[kConcurrent];
|
||||
context.count = count;
|
||||
context.oldestVersionFullPrecision = oldestVersionFullPrecision;
|
||||
context.root = root;
|
||||
context.queries = reads;
|
||||
context.results = result;
|
||||
int64_t started = std::min(kConcurrent, count);
|
||||
context.started = started;
|
||||
for (int i = 0; i < started; i++) {
|
||||
inProgress[i].init(reads + i, result + i, root,
|
||||
oldestVersionFullPrecision);
|
||||
}
|
||||
for (int i = 0; i < started - 1; i++) {
|
||||
inProgress[i].next = inProgress + i + 1;
|
||||
}
|
||||
for (int i = 1; i < started; i++) {
|
||||
inProgress[i].prev = inProgress + i - 1;
|
||||
}
|
||||
inProgress[0].prev = inProgress + started - 1;
|
||||
inProgress[started - 1].next = inProgress;
|
||||
if constexpr (kEnableInterleaved) {
|
||||
if (count == 1) {
|
||||
useSequential(reads, result, count, context);
|
||||
} else {
|
||||
constexpr int kConcurrent = 16;
|
||||
check::Job inProgress[kConcurrent];
|
||||
context.count = count;
|
||||
context.oldestVersionFullPrecision = oldestVersionFullPrecision;
|
||||
context.root = root;
|
||||
context.queries = reads;
|
||||
context.results = result;
|
||||
int64_t started = std::min(kConcurrent, count);
|
||||
context.started = started;
|
||||
for (int i = 0; i < started; i++) {
|
||||
inProgress[i].init(reads + i, result + i, root,
|
||||
oldestVersionFullPrecision);
|
||||
}
|
||||
for (int i = 0; i < started - 1; i++) {
|
||||
inProgress[i].next = inProgress + i + 1;
|
||||
}
|
||||
for (int i = 1; i < started; i++) {
|
||||
inProgress[i].prev = inProgress + i - 1;
|
||||
}
|
||||
inProgress[0].prev = inProgress + started - 1;
|
||||
inProgress[started - 1].next = inProgress;
|
||||
|
||||
// Kick off the sequence of tail calls that finally returns once all jobs
|
||||
// are done
|
||||
inProgress->continuation(inProgress, &context);
|
||||
// Kick off the sequence of tail calls that finally returns once all
|
||||
// jobs are done
|
||||
inProgress->continuation(inProgress, &context);
|
||||
|
||||
#ifndef NDEBUG
|
||||
Arena arena;
|
||||
auto *results2 = new (arena) Result[count];
|
||||
check::Context context2;
|
||||
context2.readContext.impl = this;
|
||||
useSequential(reads, results2, count, context2);
|
||||
assert(memcmp(result, results2, count) == 0);
|
||||
assert(context.readContext == context2.readContext);
|
||||
Arena arena;
|
||||
auto *results2 = new (arena) Result[count];
|
||||
check::Context context2;
|
||||
context2.readContext.impl = this;
|
||||
useSequential(reads, results2, count, context2);
|
||||
assert(memcmp(result, results2, count) == 0);
|
||||
assert(context.readContext == context2.readContext);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
useSequential(reads, result, count, context);
|
||||
}
|
||||
|
||||
#else
|
||||
useSequential(reads, result, count, context);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
assert(reads[i].readVersion >= 0);
|
||||
assert(reads[i].readVersion <= newestVersionFullPrecision);
|
||||
@@ -5186,11 +5168,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
assert(allPointWrites || sorted);
|
||||
#endif
|
||||
|
||||
#if __has_attribute(musttail)
|
||||
constexpr bool kEnableInterleaved = true;
|
||||
#else
|
||||
constexpr bool kEnableInterleaved = false;
|
||||
#endif
|
||||
if (kEnableInterleaved && count > 1) {
|
||||
interleavedWrites(writes, count, InternalVersionT(writeVersion));
|
||||
} else {
|
||||
@@ -5330,6 +5307,11 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
gc_iterations_total.add(set_oldest_iterations_accum);
|
||||
if (n == nullptr) {
|
||||
removalKey = {};
|
||||
if (removalBufferSize > kMaxRemovalBufferSize) {
|
||||
safe_free(removalBuffer, removalBufferSize);
|
||||
removalBufferSize = kMinRemovalBufferSize;
|
||||
removalBuffer = (uint8_t *)safe_malloc(removalBufferSize);
|
||||
}
|
||||
oldestExtantVersion = oldestVersionAtGcBegin;
|
||||
oldest_extant_version.set(oldestExtantVersion);
|
||||
oldestVersionAtGcBegin = oldestVersionFullPrecision;
|
||||
@@ -5340,12 +5322,47 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
oldestExtantVersion, oldestVersionAtGcBegin);
|
||||
#endif
|
||||
} else {
|
||||
removalKeyArena = Arena();
|
||||
removalKey = getSearchPath(removalKeyArena, n);
|
||||
// Store the current search path to resume the scan later
|
||||
saveRemovalKey(n);
|
||||
}
|
||||
return fuel;
|
||||
}
|
||||
|
||||
void saveRemovalKey(Node *n) {
|
||||
uint8_t *cursor = removalBuffer + removalBufferSize;
|
||||
int size = 0;
|
||||
auto reserve = [&](int delta) {
|
||||
if (size + delta > removalBufferSize) [[unlikely]] {
|
||||
int newBufSize = std::max(removalBufferSize * 2, size + delta);
|
||||
uint8_t *newBuf = (uint8_t *)safe_malloc(newBufSize);
|
||||
memcpy(newBuf + newBufSize - size, cursor, size);
|
||||
safe_free(removalBuffer, removalBufferSize);
|
||||
removalBuffer = newBuf;
|
||||
removalBufferSize = newBufSize;
|
||||
cursor = newBuf + newBufSize - size;
|
||||
}
|
||||
};
|
||||
for (;;) {
|
||||
auto partialKey = TrivialSpan{n->partialKey(), n->partialKeyLen};
|
||||
reserve(partialKey.size());
|
||||
size += partialKey.size();
|
||||
cursor -= partialKey.size();
|
||||
memcpy(cursor, partialKey.data(), partialKey.size());
|
||||
|
||||
if (n->parent == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
reserve(1);
|
||||
++size;
|
||||
--cursor;
|
||||
*cursor = n->parentsIndex;
|
||||
|
||||
n = n->parent;
|
||||
}
|
||||
removalKey = {cursor, size};
|
||||
}
|
||||
|
||||
void setOldestVersion(int64_t newOldestVersion) {
|
||||
assert(newOldestVersion >= 0);
|
||||
assert(newOldestVersion <= newestVersionFullPrecision);
|
||||
@@ -5396,7 +5413,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
writeContext.~WriteContext();
|
||||
new (&writeContext) WriteContext();
|
||||
|
||||
removalKeyArena = Arena{};
|
||||
// Leave removalBuffer as is
|
||||
removalKey = {};
|
||||
keyUpdates = 10;
|
||||
|
||||
@@ -5428,11 +5445,16 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
~Impl() {
|
||||
eraseTree(root, &writeContext);
|
||||
safe_free(metrics, metricsCount * sizeof(metrics[0]));
|
||||
safe_free(removalBuffer, removalBufferSize);
|
||||
}
|
||||
|
||||
WriteContext writeContext;
|
||||
|
||||
Arena removalKeyArena;
|
||||
static constexpr int kMinRemovalBufferSize = 1 << 10;
|
||||
// Eventually downsize if larger than this value
|
||||
static constexpr int kMaxRemovalBufferSize = 1 << 16;
|
||||
uint8_t *removalBuffer = (uint8_t *)safe_malloc(kMinRemovalBufferSize);
|
||||
int removalBufferSize = kMinRemovalBufferSize;
|
||||
TrivialSpan removalKey;
|
||||
int64_t keyUpdates;
|
||||
|
||||
@@ -5587,7 +5609,7 @@ Node *firstGeqLogical(Node *n, const TrivialSpan key) {
|
||||
if (n->entryPresent) {
|
||||
return n;
|
||||
}
|
||||
n = getFirstChildExists(n);
|
||||
n = getFirstChild(n);
|
||||
goto downLeftSpine;
|
||||
}
|
||||
|
||||
@@ -5632,7 +5654,7 @@ Node *firstGeqLogical(Node *n, const TrivialSpan key) {
|
||||
}
|
||||
}
|
||||
downLeftSpine:
|
||||
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||
for (; !n->entryPresent; n = getFirstChild(n)) {
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -5858,13 +5880,13 @@ void checkVersionsGeqOldestExtant(Node *n,
|
||||
case Type_Node0: {
|
||||
} break;
|
||||
case Type_Node3: {
|
||||
auto *self = static_cast<Node3 *>(n);
|
||||
[[maybe_unused]] auto *self = static_cast<Node3 *>(n);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||
}
|
||||
} break;
|
||||
case Type_Node16: {
|
||||
auto *self = static_cast<Node16 *>(n);
|
||||
[[maybe_unused]] auto *self = static_cast<Node16 *>(n);
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||
}
|
||||
@@ -5874,7 +5896,7 @@ void checkVersionsGeqOldestExtant(Node *n,
|
||||
for (int i = 0; i < 48; ++i) {
|
||||
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||
}
|
||||
for (auto m : self->maxOfMax) {
|
||||
for ([[maybe_unused]] auto m : self->maxOfMax) {
|
||||
assert(m >= oldestExtantVersion);
|
||||
}
|
||||
} break;
|
||||
@@ -5883,7 +5905,7 @@ void checkVersionsGeqOldestExtant(Node *n,
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||
}
|
||||
for (auto m : self->maxOfMax) {
|
||||
for ([[maybe_unused]] auto m : self->maxOfMax) {
|
||||
assert(m >= oldestExtantVersion);
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -13,12 +13,14 @@ RUN TZ=America/Los_Angeles DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
ccache \
|
||||
cmake \
|
||||
curl \
|
||||
devscripts \
|
||||
g++-aarch64-linux-gnu \
|
||||
gcovr \
|
||||
git \
|
||||
gnupg \
|
||||
libc6-dbg \
|
||||
lsb-release \
|
||||
mold \
|
||||
ninja-build \
|
||||
pre-commit \
|
||||
python3-requests \
|
||||
|
||||
18
Internal.h
18
Internal.h
@@ -18,7 +18,6 @@ using namespace weaselab;
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include <callgrind.h>
|
||||
@@ -368,23 +367,6 @@ template <class T, class C = std::less<T>> auto set(Arena &arena) {
|
||||
return Set<T, C>(ArenaAlloc<T>(&arena));
|
||||
}
|
||||
|
||||
template <class T> struct MyHash;
|
||||
|
||||
template <class T> struct MyHash<T *> {
|
||||
size_t operator()(const T *t) const noexcept {
|
||||
size_t result;
|
||||
memcpy(&result, &t, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using HashSet =
|
||||
std::unordered_set<T, MyHash<T>, std::equal_to<T>, ArenaAlloc<T>>;
|
||||
template <class T> auto hashSet(Arena &arena) {
|
||||
return HashSet<T>(ArenaAlloc<T>(&arena));
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(const ArenaAlloc<T> &lhs, const ArenaAlloc<U> &rhs) {
|
||||
return lhs.arena == rhs.arena;
|
||||
|
||||
4
Jenkinsfile
vendored
4
Jenkinsfile
vendored
@@ -91,7 +91,7 @@ pipeline {
|
||||
minio bucket: 'jenkins', credentialsId: 'jenkins-minio', excludes: '', host: 'minio.weaselab.dev', includes: 'build/*.deb,build/*.rpm,paper/*.pdf', targetFolder: '${JOB_NAME}/${BUILD_NUMBER}/${STAGE_NAME}/'
|
||||
}
|
||||
}
|
||||
stage('Release [gcc]') {
|
||||
stage('gcc') {
|
||||
agent {
|
||||
dockerfile {
|
||||
args '-v /home/jenkins/ccache:/ccache'
|
||||
@@ -99,7 +99,7 @@ pipeline {
|
||||
}
|
||||
}
|
||||
steps {
|
||||
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS=-DNVALGRIND")
|
||||
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++")
|
||||
recordIssues(tools: [gcc()])
|
||||
}
|
||||
}
|
||||
|
||||
55
README.md
55
README.md
@@ -4,7 +4,14 @@ Intended as an alternative to FoundationDB's skip list.
|
||||
|
||||
Hardware for all benchmarks is an AMD Ryzen 9 7900 with (2x32GB) 5600MT/s CL28-34-34-89 1.35V RAM.
|
||||
|
||||
Compiler is `Ubuntu clang version 20.0.0 (++20241029082144+7544d3af0e28-1~exp1~20241029082307.506)`.
|
||||
```
|
||||
$ clang++ --version
|
||||
|
||||
Ubuntu clang version 20.0.0 (++20241119082716+7e85cb8a8a9d-1~exp1~20241119082825.551)
|
||||
Target: x86_64-pc-linux-gnu
|
||||
Thread model: posix
|
||||
InstalledDir: /usr/lib/llvm-20/bin
|
||||
```
|
||||
|
||||
# Microbenchmark
|
||||
|
||||
@@ -12,44 +19,45 @@ Compiler is `Ubuntu clang version 20.0.0 (++20241029082144+7544d3af0e28-1~exp1~2
|
||||
|
||||
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
||||
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
||||
| 159.65 | 6,263,576.52 | 1.6% | 2,972.36 | 820.37 | 3.623 | 504.59 | 0.0% | 0.01 | `point reads`
|
||||
| 156.32 | 6,397,320.65 | 0.7% | 2,913.62 | 806.87 | 3.611 | 490.19 | 0.0% | 0.01 | `prefix reads`
|
||||
| 229.18 | 4,363,293.65 | 1.2% | 3,541.05 | 1,219.75 | 2.903 | 629.33 | 0.0% | 0.01 | `range reads`
|
||||
| 363.37 | 2,752,026.30 | 0.3% | 5,273.63 | 1,951.54 | 2.702 | 851.66 | 1.7% | 0.01 | `point writes`
|
||||
| 364.99 | 2,739,787.02 | 0.3% | 5,250.92 | 1,958.54 | 2.681 | 839.24 | 1.7% | 0.01 | `prefix writes`
|
||||
| 242.26 | 4,127,796.58 | 2.9% | 3,117.33 | 1,304.41 | 2.390 | 541.07 | 2.8% | 0.02 | `range writes`
|
||||
| 562.48 | 1,777,855.27 | 0.8% | 7,305.21 | 3,034.34 | 2.408 | 1,329.30 | 1.3% | 0.01 | `monotonic increasing point writes`
|
||||
| 122,688.57 | 8,150.72 | 0.7% | 798,766.00 | 666,842.00 | 1.198 | 144,584.50 | 0.1% | 0.01 | `worst case for radix tree`
|
||||
| 41.71 | 23,976,459.34 | 1.7% | 885.00 | 219.17 | 4.038 | 132.00 | 0.0% | 0.01 | `create and destroy`
|
||||
| 169.16 | 5,911,582.44 | 0.0% | 3,014.03 | 855.12 | 3.525 | 504.59 | 0.0% | 2.02 | `point reads`
|
||||
| 167.17 | 5,981,796.19 | 0.0% | 2,954.16 | 845.14 | 3.495 | 490.17 | 0.0% | 2.00 | `prefix reads`
|
||||
| 250.44 | 3,992,954.35 | 0.1% | 3,592.41 | 1,265.18 | 2.839 | 629.31 | 0.0% | 2.99 | `range reads`
|
||||
| 467.10 | 2,140,846.36 | 0.0% | 4,450.57 | 2,488.36 | 1.789 | 707.92 | 2.1% | 5.62 | `point writes`
|
||||
| 465.18 | 2,149,723.11 | 0.2% | 4,410.22 | 2,474.92 | 1.782 | 694.74 | 2.1% | 5.55 | `prefix writes`
|
||||
| 297.45 | 3,361,954.05 | 0.1% | 2,315.38 | 1,581.64 | 1.464 | 396.69 | 3.3% | 3.57 | `range writes`
|
||||
| 476.56 | 2,098,370.82 | 1.0% | 6,999.33 | 2,492.26 | 2.808 | 1,251.74 | 1.3% | 0.06 | `monotonic increasing point writes`
|
||||
| 129,455.00 | 7,724.69 | 1.0% | 807,446.67 | 698,559.40 | 1.156 | 144,584.60 | 0.8% | 0.01 | `worst case for radix tree`
|
||||
| 44.67 | 22,384,996.63 | 0.5% | 902.00 | 235.18 | 3.835 | 132.00 | 0.0% | 0.01 | `create and destroy`
|
||||
|
||||
## Radix tree (this implementation)
|
||||
|
||||
|
||||
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
||||
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
||||
| 12.63 | 79,186,868.18 | 1.4% | 241.61 | 64.76 | 3.731 | 31.64 | 0.8% | 0.01 | `point reads`
|
||||
| 14.48 | 69,078,073.40 | 0.3% | 292.42 | 74.69 | 3.915 | 41.49 | 0.5% | 0.01 | `prefix reads`
|
||||
| 34.37 | 29,094,694.11 | 0.2% | 759.53 | 179.77 | 4.225 | 100.38 | 0.2% | 0.01 | `range reads`
|
||||
| 19.34 | 51,713,896.36 | 0.7% | 369.70 | 101.81 | 3.631 | 47.88 | 0.6% | 0.01 | `point writes`
|
||||
| 39.16 | 25,538,968.61 | 0.2% | 653.16 | 206.77 | 3.159 | 89.62 | 0.8% | 0.01 | `prefix writes`
|
||||
| 40.58 | 24,642,681.12 | 4.7% | 718.44 | 216.44 | 3.319 | 99.28 | 0.6% | 0.01 | `range writes`
|
||||
| 78.77 | 12,694,520.69 | 3.8% | 1,395.55 | 421.73 | 3.309 | 249.81 | 0.1% | 0.01 | `monotonic increasing point writes`
|
||||
| 287,760.50 | 3,475.11 | 0.5% | 3,929,266.50 | 1,550,225.50 | 2.535 | 639,064.00 | 0.0% | 0.01 | `worst case for radix tree`
|
||||
| 104.76 | 9,545,250.65 | 3.1% | 2,000.00 | 552.82 | 3.618 | 342.00 | 0.0% | 0.01 | `create and destroy`
|
||||
| 14.11 | 70,857,435.19 | 0.1% | 247.13 | 71.03 | 3.479 | 32.64 | 0.8% | 0.17 | `point reads`
|
||||
| 15.63 | 63,997,306.79 | 0.0% | 299.99 | 78.59 | 3.817 | 42.50 | 0.4% | 0.19 | `prefix reads`
|
||||
| 36.24 | 27,590,266.59 | 0.1% | 782.70 | 182.21 | 4.296 | 106.65 | 0.2% | 0.43 | `range reads`
|
||||
| 22.72 | 44,004,627.40 | 0.1% | 376.04 | 114.33 | 3.289 | 49.97 | 0.8% | 0.27 | `point writes`
|
||||
| 40.83 | 24,494,110.04 | 0.0% | 666.07 | 205.35 | 3.244 | 101.33 | 0.3% | 0.49 | `prefix writes`
|
||||
| 43.45 | 23,016,324.00 | 0.0% | 732.33 | 218.41 | 3.353 | 111.64 | 0.1% | 0.53 | `range writes`
|
||||
| 81.46 | 12,276,650.63 | 3.6% | 1,458.85 | 411.52 | 3.545 | 280.42 | 0.1% | 0.01 | `monotonic increasing point writes`
|
||||
| 314,217.00 | 3,182.51 | 1.2% | 4,043,063.50 | 1,593,715.00 | 2.537 | 714,828.00 | 0.1% | 0.01 | `worst case for radix tree`
|
||||
| 106.79 | 9,364,602.60 | 0.5% | 2,046.00 | 539.75 | 3.791 | 329.00 | 0.0% | 0.01 | `create and destroy`
|
||||
|
||||
# "Real data" test
|
||||
|
||||
Point queries only, best of three runs. Gc ratio is the ratio of time spent doing garbage collection to time spent adding writes or doing garbage collection. Lower is better.
|
||||
Point queries only. Gc ratio is the ratio of time spent doing garbage collection to time spent adding writes or doing garbage collection. Lower is better.
|
||||
|
||||
## skip list
|
||||
|
||||
```
|
||||
Check: 4.39702 seconds, 370.83 MB/s, Add: 4.50025 seconds, 124.583 MB/s, Gc ratio: 29.1333%, Peak idle memory: 5.51852e+06
|
||||
Check: 4.62434 seconds, 364.633 MB/s, Add: 3.90399 seconds, 147.371 MB/s, Gc ratio: 33.6898%, Peak idle memory: 5.61007e+06
|
||||
```
|
||||
|
||||
## radix tree
|
||||
|
||||
```
|
||||
Check: 0.987757 seconds, 1650.76 MB/s, Add: 1.24815 seconds, 449.186 MB/s, Gc ratio: 41.4675%, Peak idle memory: 2.02872e+06
|
||||
Check: 0.956689 seconds, 1762.52 MB/s, Add: 1.35744 seconds, 423.84 MB/s, Gc ratio: 35.0946%, Peak idle memory: 2.32922e+06
|
||||
```
|
||||
|
||||
## hash table
|
||||
@@ -57,5 +65,6 @@ Check: 0.987757 seconds, 1650.76 MB/s, Add: 1.24815 seconds, 449.186 MB/s, Gc ra
|
||||
(The hash table implementation doesn't work on range queries, and its purpose is to provide an idea of how fast point queries can be)
|
||||
|
||||
```
|
||||
Check: 0.84256 seconds, 1935.23 MB/s, Add: 0.697204 seconds, 804.146 MB/s, Gc ratio: 35.4091%
|
||||
Check: 0.799863 seconds, 2108.09 MB/s, Add: 0.667736 seconds, 861.621 MB/s, Gc ratio: 35.0666%, Peak idle memory: 0
|
||||
```
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
@@ -64,7 +64,7 @@ int main(int argc, const char **argv) {
|
||||
auto *const mapOriginal = begin;
|
||||
const auto sizeOriginal = size;
|
||||
|
||||
using StringView = std::basic_string_view<uint8_t>;
|
||||
using StringView = std::span<const uint8_t>;
|
||||
|
||||
StringView write;
|
||||
std::vector<StringView> reads;
|
||||
@@ -78,9 +78,9 @@ int main(int argc, const char **argv) {
|
||||
end = (uint8_t *)memchr(begin, '\n', size);
|
||||
|
||||
if (line.size() > 0 && line[0] == 'P') {
|
||||
write = line.substr(2, line.size());
|
||||
write = line.subspan(2, line.size());
|
||||
} else if (line.size() > 0 && line[0] == 'L') {
|
||||
reads.push_back(line.substr(2, line.size()));
|
||||
reads.push_back(line.subspan(2, line.size()));
|
||||
} else if (line.empty()) {
|
||||
{
|
||||
readRanges.resize(reads.size());
|
||||
@@ -133,10 +133,10 @@ int main(int argc, const char **argv) {
|
||||
int metricsCount;
|
||||
cs.getMetricsV1(&metrics, &metricsCount);
|
||||
for (int i = 0; i < metricsCount; ++i) {
|
||||
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||
printf("# TYPE %s %s\n", metrics[i].name,
|
||||
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
||||
printf("%s %g\n", metrics[i].name, metrics[i].getValue());
|
||||
fprintf(stderr, "# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||
fprintf(stderr, "# TYPE %s %s\n", metrics[i].name,
|
||||
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
||||
fprintf(stderr, "%s %g\n", metrics[i].name, metrics[i].getValue());
|
||||
}
|
||||
|
||||
printf("Check: %g seconds, %g MB/s, Add: %g seconds, %g MB/s, Gc ratio: "
|
||||
|
||||
201
ServerBench.cpp
201
ServerBench.cpp
@@ -23,6 +23,97 @@
|
||||
#include "Internal.h"
|
||||
#include "third_party/nadeau.h"
|
||||
|
||||
constexpr int kCacheLine = 64; // TODO mac m1 is 128
|
||||
|
||||
template <class T> struct TxQueue {
|
||||
|
||||
explicit TxQueue(int lgSlotCount)
|
||||
: slotCount(1 << lgSlotCount), slotCountMask(slotCount - 1),
|
||||
slots(new T[slotCount]) {
|
||||
// Otherwise we can't tell the difference between full and empty.
|
||||
assert(!(slotCountMask & 0x80000000));
|
||||
}
|
||||
|
||||
/// Call from producer thread, after ensuring consumer is no longer accessing
|
||||
/// it somehow
|
||||
~TxQueue() { delete[] slots; }
|
||||
|
||||
/// Must be called from the producer thread
|
||||
void push(T t) {
|
||||
if (wouldBlock()) {
|
||||
// Wait for pops to change and try again
|
||||
consumer.pops.wait(producer.lastPopRead, std::memory_order_relaxed);
|
||||
producer.lastPopRead = consumer.pops.load(std::memory_order_acquire);
|
||||
}
|
||||
slots[producer.pushesNonAtomic++ & slotCountMask] = std::move(t);
|
||||
// seq_cst so that the notify can't be ordered before the store
|
||||
producer.pushes.store(producer.pushesNonAtomic, std::memory_order_seq_cst);
|
||||
// We have to notify every time, since we don't know if this is the last
|
||||
// push ever
|
||||
producer.pushes.notify_one();
|
||||
}
|
||||
|
||||
/// Must be called from the producer thread
|
||||
uint32_t outstanding() {
|
||||
return producer.pushesNonAtomic -
|
||||
consumer.pops.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/// Returns true if a call to push might block. Must be called from the
|
||||
/// producer thread.
|
||||
bool wouldBlock() {
|
||||
// See if we can determine that overflow won't happen entirely from state
|
||||
// local to the producer
|
||||
if (producer.pushesNonAtomic - producer.lastPopRead == slotCount - 1) {
|
||||
// Re-read pops with memory order
|
||||
producer.lastPopRead = consumer.pops.load(std::memory_order_acquire);
|
||||
return producer.pushesNonAtomic - producer.lastPopRead == slotCount - 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Valid until the next pop, or until this queue is destroyed.
|
||||
T *pop() {
|
||||
// See if we can determine that there's an entry we can pop entirely from
|
||||
// state local to the consumer
|
||||
if (consumer.lastPushRead - consumer.popsNonAtomic == 0) {
|
||||
// Re-read pushes with memory order and try again
|
||||
consumer.lastPushRead = producer.pushes.load(std::memory_order_acquire);
|
||||
if (consumer.lastPushRead - consumer.popsNonAtomic == 0) {
|
||||
// Wait for pushes to change and try again
|
||||
producer.pushes.wait(consumer.lastPushRead, std::memory_order_relaxed);
|
||||
consumer.lastPushRead = producer.pushes.load(std::memory_order_acquire);
|
||||
}
|
||||
}
|
||||
auto result = &slots[consumer.popsNonAtomic++ & slotCountMask];
|
||||
// We only have to write pops with memory order if we've run out of items.
|
||||
// We know that we'll eventually run out.
|
||||
if (consumer.lastPushRead - consumer.popsNonAtomic == 0) {
|
||||
// seq_cst so that the notify can't be ordered before the store
|
||||
consumer.pops.store(consumer.popsNonAtomic, std::memory_order_seq_cst);
|
||||
consumer.pops.notify_one();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t slotCount;
|
||||
const uint32_t slotCountMask;
|
||||
T *slots;
|
||||
struct alignas(kCacheLine) ProducerState {
|
||||
std::atomic<uint32_t> pushes{0};
|
||||
uint32_t pushesNonAtomic{0};
|
||||
uint32_t lastPopRead{0};
|
||||
};
|
||||
struct alignas(kCacheLine) ConsumerState {
|
||||
std::atomic<uint32_t> pops{0};
|
||||
uint32_t popsNonAtomic{0};
|
||||
uint32_t lastPushRead{0};
|
||||
};
|
||||
ProducerState producer;
|
||||
ConsumerState consumer;
|
||||
};
|
||||
|
||||
std::atomic<int64_t> transactions;
|
||||
|
||||
int64_t safeUnaryMinus(int64_t x) {
|
||||
@@ -47,13 +138,17 @@ void tupleAppend(std::string &output, int64_t value) {
|
||||
|
||||
void tupleAppend(std::string &output, std::string_view value) {
|
||||
output.push_back('\x02');
|
||||
for (auto c : value) {
|
||||
if (c == '\x00') {
|
||||
output.push_back('\x00');
|
||||
output.push_back('\xff');
|
||||
} else {
|
||||
output.push_back(c);
|
||||
if (memchr(value.data(), '\x00', value.size()) != nullptr) {
|
||||
for (auto c : value) {
|
||||
if (c == '\x00') {
|
||||
output.push_back('\x00');
|
||||
output.push_back('\xff');
|
||||
} else {
|
||||
output.push_back(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
output.insert(output.end(), value.begin(), value.end());
|
||||
}
|
||||
output.push_back('\x00');
|
||||
}
|
||||
@@ -64,35 +159,71 @@ template <class... Ts> std::string tupleKey(const Ts &...ts) {
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr int kWindowSize = 300000;
|
||||
constexpr int kTotalKeyRange = 1'000'000'000;
|
||||
constexpr int kWindowSize = 1'000'000;
|
||||
constexpr int kNumReadKeysPerTx = 10;
|
||||
constexpr int kNumWriteKeysPerTx = 5;
|
||||
|
||||
void workload(weaselab::ConflictSet *cs) {
|
||||
int64_t version = kWindowSize;
|
||||
constexpr int kNumWrites = 16;
|
||||
for (;; transactions.fetch_add(1, std::memory_order_relaxed)) {
|
||||
struct Transaction {
|
||||
std::vector<std::string> keys;
|
||||
std::vector<weaselab::ConflictSet::ReadRange> reads;
|
||||
std::vector<weaselab::ConflictSet::WriteRange> writes;
|
||||
int64_t version;
|
||||
int64_t oldestVersion;
|
||||
Transaction() = default;
|
||||
explicit Transaction(int64_t version)
|
||||
: version(version), oldestVersion(version - kWindowSize) {
|
||||
std::vector<int64_t> keyIndices;
|
||||
for (int i = 0; i < kNumWrites; ++i) {
|
||||
keyIndices.push_back(rand() % 100'000'000);
|
||||
for (int i = 0; i < std::max(kNumReadKeysPerTx, kNumWriteKeysPerTx); ++i) {
|
||||
keyIndices.push_back(rand() % kTotalKeyRange);
|
||||
}
|
||||
std::sort(keyIndices.begin(), keyIndices.end());
|
||||
std::vector<std::string> keys;
|
||||
std::vector<weaselab::ConflictSet::WriteRange> writes;
|
||||
constexpr std::string_view suffix = "this is a suffix";
|
||||
for (int i = 0; i < kNumWrites; ++i) {
|
||||
keys.push_back(tupleKey(0x100, i, keyIndices[i],
|
||||
suffix.substr(0, rand() % suffix.size()),
|
||||
rand()));
|
||||
constexpr std::string_view fullString =
|
||||
"this is a string, where a prefix of it is used as an element of the "
|
||||
"tuple forming the key";
|
||||
for (int i = 0; i < int(keyIndices.size()); ++i) {
|
||||
keys.push_back(
|
||||
tupleKey(0x100, keyIndices[i] / fullString.size(),
|
||||
fullString.substr(0, keyIndices[i] % fullString.size())));
|
||||
// printf("%s\n", printable(keys.back()).c_str());
|
||||
}
|
||||
for (int i = 0; i < kNumWrites; ++i) {
|
||||
for (int i = 0; i < kNumWriteKeysPerTx; ++i) {
|
||||
writes.push_back({{(const uint8_t *)keys[i].data(), int(keys[i].size())},
|
||||
{nullptr, 0}});
|
||||
}
|
||||
cs->addWrites(writes.data(), writes.size(), version);
|
||||
cs->setOldestVersion(version - kWindowSize);
|
||||
++version;
|
||||
reads.push_back({{(const uint8_t *)keys[0].data(), int(keys[0].size())},
|
||||
{(const uint8_t *)keys[1].data(), int(keys[1].size())},
|
||||
version - std::min(10, kWindowSize)});
|
||||
static_assert(kNumReadKeysPerTx >= 3);
|
||||
for (int i = 2; i < kNumReadKeysPerTx; ++i) {
|
||||
reads.push_back({{(const uint8_t *)keys[i].data(), int(keys[i].size())},
|
||||
{nullptr, 0},
|
||||
version - kWindowSize});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Transaction(Transaction &&) = default;
|
||||
Transaction &operator=(Transaction &&) = default;
|
||||
Transaction(Transaction const &) = delete;
|
||||
Transaction const &operator=(Transaction const &) = delete;
|
||||
};
|
||||
|
||||
struct Resolver {
|
||||
|
||||
void resolve(const weaselab::ConflictSet::ReadRange *reads, int readCount,
|
||||
const weaselab::ConflictSet::WriteRange *writes, int writeCount,
|
||||
int64_t newVersion, int64_t newOldestVersion) {
|
||||
results.resize(readCount);
|
||||
cs.check(reads, results.data(), readCount);
|
||||
cs.addWrites(writes, writeCount, newVersion);
|
||||
cs.setOldestVersion(newOldestVersion);
|
||||
}
|
||||
|
||||
ConflictSet cs{0};
|
||||
|
||||
private:
|
||||
std::vector<weaselab::ConflictSet::Result> results;
|
||||
};
|
||||
|
||||
// Adapted from getaddrinfo man page
|
||||
int getListenFd(const char *node, const char *service) {
|
||||
@@ -235,7 +366,8 @@ int main(int argc, char **argv) {
|
||||
{
|
||||
int listenFd = getListenFd(argv[1], argv[2]);
|
||||
|
||||
weaselab::ConflictSet cs{0};
|
||||
Resolver resolver;
|
||||
auto &cs = resolver.cs;
|
||||
weaselab::ConflictSet::MetricsV1 *metrics;
|
||||
int metricsCount;
|
||||
cs.getMetricsV1(&metrics, &metricsCount);
|
||||
@@ -284,7 +416,22 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
#endif
|
||||
|
||||
auto w = std::thread{workload, &cs};
|
||||
TxQueue<Transaction> queue{10};
|
||||
|
||||
auto workloadThread = std::thread{[&]() {
|
||||
for (int64_t version = kWindowSize;;
|
||||
++version, transactions.fetch_add(1, std::memory_order_relaxed)) {
|
||||
queue.push(Transaction(version));
|
||||
}
|
||||
}};
|
||||
|
||||
auto resolverThread = std::thread{[&]() {
|
||||
for (;;) {
|
||||
auto tx = queue.pop();
|
||||
resolver.resolve(tx->reads.data(), tx->reads.size(), tx->writes.data(),
|
||||
tx->writes.size(), tx->version, tx->oldestVersion);
|
||||
}
|
||||
}};
|
||||
|
||||
for (;;) {
|
||||
struct sockaddr_storage peer_addr = {};
|
||||
|
||||
@@ -767,7 +767,9 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
false, true);
|
||||
}
|
||||
|
||||
sortPoints(points);
|
||||
if (!std::is_sorted(points.begin(), points.end())) {
|
||||
sortPoints(points);
|
||||
}
|
||||
|
||||
int activeWriteCount = 0;
|
||||
std::vector<std::pair<StringRef, StringRef>> combinedWriteConflictRanges;
|
||||
@@ -794,7 +796,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
int temp[stripeSize];
|
||||
int stripes = (stringCount + stripeSize - 1) / stripeSize;
|
||||
StringRef values[stripeSize];
|
||||
int64_t writeVersions[stripeSize / 2];
|
||||
int ss = stringCount - (stripes - 1) * stripeSize;
|
||||
int64_t entryDelta = 0;
|
||||
for (int s = stripes - 1; s >= 0; s--) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
___chkstk_darwin
|
||||
___stack_chk_fail
|
||||
___stack_chk_guard
|
||||
__tlv_bootstrap
|
||||
@@ -5,6 +6,7 @@ _abort
|
||||
_bzero
|
||||
_free
|
||||
_malloc
|
||||
_memcmp
|
||||
_memcpy
|
||||
_memmove
|
||||
dyld_stub_binder
|
||||
BIN
corpus/03d3918b737a86ed38fbeae6dff198d6913b90b2
Normal file
BIN
corpus/03d3918b737a86ed38fbeae6dff198d6913b90b2
Normal file
Binary file not shown.
BIN
corpus/0b93fbf7c443c8403e0c3ee6cd3d9498aa9aaeb0
Normal file
BIN
corpus/0b93fbf7c443c8403e0c3ee6cd3d9498aa9aaeb0
Normal file
Binary file not shown.
BIN
corpus/0d35b148f45f7e3f722f4ac298558ba0dd545b48
Normal file
BIN
corpus/0d35b148f45f7e3f722f4ac298558ba0dd545b48
Normal file
Binary file not shown.
BIN
corpus/0eb983ee4da4177894988ab187e0d17719df3c3e
Normal file
BIN
corpus/0eb983ee4da4177894988ab187e0d17719df3c3e
Normal file
Binary file not shown.
BIN
corpus/136ad900e11b346cf1ef2d3b0a2a94ce6a17e797
Normal file
BIN
corpus/136ad900e11b346cf1ef2d3b0a2a94ce6a17e797
Normal file
Binary file not shown.
BIN
corpus/1c65a17a05d620dd0b46c63d71d0febfbbb6aeb6
Normal file
BIN
corpus/1c65a17a05d620dd0b46c63d71d0febfbbb6aeb6
Normal file
Binary file not shown.
BIN
corpus/1e7a4ca606559d15183818a6c3d93b2f132b2fff
Normal file
BIN
corpus/1e7a4ca606559d15183818a6c3d93b2f132b2fff
Normal file
Binary file not shown.
BIN
corpus/25046edfa84c50539e352d8a5c14d5a38cbccbc9
Normal file
BIN
corpus/25046edfa84c50539e352d8a5c14d5a38cbccbc9
Normal file
Binary file not shown.
BIN
corpus/2516ed82e09a82fb3208befc7bbec866b45709ad
Normal file
BIN
corpus/2516ed82e09a82fb3208befc7bbec866b45709ad
Normal file
Binary file not shown.
BIN
corpus/25aac6502b651821dbd979eb57bf6b0b58fd5a62
Normal file
BIN
corpus/25aac6502b651821dbd979eb57bf6b0b58fd5a62
Normal file
Binary file not shown.
BIN
corpus/26ac1ea5ad85e43ed4aff18c34b6622a5f6982ce
Normal file
BIN
corpus/26ac1ea5ad85e43ed4aff18c34b6622a5f6982ce
Normal file
Binary file not shown.
BIN
corpus/2d98ecbaadf6adcba6d9dbb5de15436dfa92df6e
Normal file
BIN
corpus/2d98ecbaadf6adcba6d9dbb5de15436dfa92df6e
Normal file
Binary file not shown.
BIN
corpus/30c2bb0e617ff9a8b3ab252971be3fdc61f58e9b
Normal file
BIN
corpus/30c2bb0e617ff9a8b3ab252971be3fdc61f58e9b
Normal file
Binary file not shown.
BIN
corpus/3349a2cb0de93c001fc81dd09b795b7bb2911cb2
Normal file
BIN
corpus/3349a2cb0de93c001fc81dd09b795b7bb2911cb2
Normal file
Binary file not shown.
BIN
corpus/3388003130da408556ca4ebbe6f1e3cc3e110b33
Normal file
BIN
corpus/3388003130da408556ca4ebbe6f1e3cc3e110b33
Normal file
Binary file not shown.
BIN
corpus/342740d94427af6509e3332d46d99e1091a5c065
Normal file
BIN
corpus/342740d94427af6509e3332d46d99e1091a5c065
Normal file
Binary file not shown.
BIN
corpus/3571178de127e769d6229057b205f9df36506cbd
Normal file
BIN
corpus/3571178de127e769d6229057b205f9df36506cbd
Normal file
Binary file not shown.
BIN
corpus/36bc3a1544e6b3564de3e1944274541ea8e44346
Normal file
BIN
corpus/36bc3a1544e6b3564de3e1944274541ea8e44346
Normal file
Binary file not shown.
BIN
corpus/3c1a9cec8435cdf21b6d0ae9635dfc73df7f9c50
Normal file
BIN
corpus/3c1a9cec8435cdf21b6d0ae9635dfc73df7f9c50
Normal file
Binary file not shown.
BIN
corpus/3ed5e36f67e3dcd0aaf17d62eb2d3fc877c95510
Normal file
BIN
corpus/3ed5e36f67e3dcd0aaf17d62eb2d3fc877c95510
Normal file
Binary file not shown.
BIN
corpus/40fe1d32a464e3c8ec822d6cca93bff1cbad6d95
Normal file
BIN
corpus/40fe1d32a464e3c8ec822d6cca93bff1cbad6d95
Normal file
Binary file not shown.
BIN
corpus/42620ad6039a83a92d5f5c5c8f764c59149a0852
Normal file
BIN
corpus/42620ad6039a83a92d5f5c5c8f764c59149a0852
Normal file
Binary file not shown.
BIN
corpus/4741ed549a33458638d6b57c62521d565cee1d60
Normal file
BIN
corpus/4741ed549a33458638d6b57c62521d565cee1d60
Normal file
Binary file not shown.
BIN
corpus/480df5b3fbac685e168a0b403459c331d0ca9dba
Normal file
BIN
corpus/480df5b3fbac685e168a0b403459c331d0ca9dba
Normal file
Binary file not shown.
BIN
corpus/486d0d68d44e9eb5ecfc9d64d23ae561dfc150ad
Normal file
BIN
corpus/486d0d68d44e9eb5ecfc9d64d23ae561dfc150ad
Normal file
Binary file not shown.
BIN
corpus/49b5820ef82fd586e356cc210feb5c45219de99a
Normal file
BIN
corpus/49b5820ef82fd586e356cc210feb5c45219de99a
Normal file
Binary file not shown.
BIN
corpus/4a2e895a63fd0487d9115aac49305cfad276d901
Normal file
BIN
corpus/4a2e895a63fd0487d9115aac49305cfad276d901
Normal file
Binary file not shown.
BIN
corpus/4a8c3da6a5ca2f76c747445a289526d654df9b5b
Normal file
BIN
corpus/4a8c3da6a5ca2f76c747445a289526d654df9b5b
Normal file
Binary file not shown.
BIN
corpus/545b54c3b37d81297c38e839d2933bfb67caea01
Normal file
BIN
corpus/545b54c3b37d81297c38e839d2933bfb67caea01
Normal file
Binary file not shown.
BIN
corpus/556079270f90fd93f2f52c2a88a2cad39f9906e9
Normal file
BIN
corpus/556079270f90fd93f2f52c2a88a2cad39f9906e9
Normal file
Binary file not shown.
BIN
corpus/557f000e67b09ad7647e480e5ca51c39c6fb56fd
Normal file
BIN
corpus/557f000e67b09ad7647e480e5ca51c39c6fb56fd
Normal file
Binary file not shown.
BIN
corpus/5773401508187fb620acd8e7d2b10401f41ab527
Normal file
BIN
corpus/5773401508187fb620acd8e7d2b10401f41ab527
Normal file
Binary file not shown.
BIN
corpus/59eb125178004fa691678fa4aeab2acf9eb20d92
Normal file
BIN
corpus/59eb125178004fa691678fa4aeab2acf9eb20d92
Normal file
Binary file not shown.
BIN
corpus/5c9267b106f81105ccd6ff67115a494ad967d31c
Normal file
BIN
corpus/5c9267b106f81105ccd6ff67115a494ad967d31c
Normal file
Binary file not shown.
BIN
corpus/613b3f22e9c850e88e43d4a08ab3f4aa690db94a
Normal file
BIN
corpus/613b3f22e9c850e88e43d4a08ab3f4aa690db94a
Normal file
Binary file not shown.
BIN
corpus/615c446c969544c9334729cdc8c8ece56f8d5b16
Normal file
BIN
corpus/615c446c969544c9334729cdc8c8ece56f8d5b16
Normal file
Binary file not shown.
BIN
corpus/638968595f06cd59f8654d24f043d0f80c30f2ea
Normal file
BIN
corpus/638968595f06cd59f8654d24f043d0f80c30f2ea
Normal file
Binary file not shown.
BIN
corpus/6ca1fb9210b8478a854ec5406201b582c3c45dac
Normal file
BIN
corpus/6ca1fb9210b8478a854ec5406201b582c3c45dac
Normal file
Binary file not shown.
BIN
corpus/6ea3ee71e02ee8689b185816d0166c83a8036cf4
Normal file
BIN
corpus/6ea3ee71e02ee8689b185816d0166c83a8036cf4
Normal file
Binary file not shown.
BIN
corpus/72e3ccd7785f7ee34adbc41ad0fe37a4e3997fa8
Normal file
BIN
corpus/72e3ccd7785f7ee34adbc41ad0fe37a4e3997fa8
Normal file
Binary file not shown.
BIN
corpus/7343aeff21ad270485906be46e6c2398c6af46cd
Normal file
BIN
corpus/7343aeff21ad270485906be46e6c2398c6af46cd
Normal file
Binary file not shown.
BIN
corpus/7503ae513fff011cd410b8838e15d844567cdba0
Normal file
BIN
corpus/7503ae513fff011cd410b8838e15d844567cdba0
Normal file
Binary file not shown.
BIN
corpus/76f4456c1817bdfd54d6a6731f41b5c7951df797
Normal file
BIN
corpus/76f4456c1817bdfd54d6a6731f41b5c7951df797
Normal file
Binary file not shown.
BIN
corpus/7901afce389a39f171fc4689e801ffc1198aa550
Normal file
BIN
corpus/7901afce389a39f171fc4689e801ffc1198aa550
Normal file
Binary file not shown.
BIN
corpus/7bf8c8d06451c512507d251d477c3ec61bb869f2
Normal file
BIN
corpus/7bf8c8d06451c512507d251d477c3ec61bb869f2
Normal file
Binary file not shown.
BIN
corpus/7ed5a81d9e6335cc454f2c94b9a49cc293ac245e
Normal file
BIN
corpus/7ed5a81d9e6335cc454f2c94b9a49cc293ac245e
Normal file
Binary file not shown.
BIN
corpus/80fb897a23d1bbce326a8ae8ed257559d1dee707
Normal file
BIN
corpus/80fb897a23d1bbce326a8ae8ed257559d1dee707
Normal file
Binary file not shown.
BIN
corpus/84ec63b6bdc2fdb26f345a3c45684d1402a48281
Normal file
BIN
corpus/84ec63b6bdc2fdb26f345a3c45684d1402a48281
Normal file
Binary file not shown.
BIN
corpus/853694cf1e93f28aca05f2e96798ece03061d8a9
Normal file
BIN
corpus/853694cf1e93f28aca05f2e96798ece03061d8a9
Normal file
Binary file not shown.
BIN
corpus/8be471d8ac1cad83fb77e32bbfe5db1718c7e92c
Normal file
BIN
corpus/8be471d8ac1cad83fb77e32bbfe5db1718c7e92c
Normal file
Binary file not shown.
BIN
corpus/9078ea2ea49ecbfda8dd547b82175f4f2cec85d0
Normal file
BIN
corpus/9078ea2ea49ecbfda8dd547b82175f4f2cec85d0
Normal file
Binary file not shown.
BIN
corpus/96e2a3c9f8d6cc448d67502664f737560b422671
Normal file
BIN
corpus/96e2a3c9f8d6cc448d67502664f737560b422671
Normal file
Binary file not shown.
BIN
corpus/9d22999d454fad771740dc5c9cfb187e9bfb49d7
Normal file
BIN
corpus/9d22999d454fad771740dc5c9cfb187e9bfb49d7
Normal file
Binary file not shown.
BIN
corpus/9deef5d7e40663e48c50d5049014e84c3a8ec72f
Normal file
BIN
corpus/9deef5d7e40663e48c50d5049014e84c3a8ec72f
Normal file
Binary file not shown.
BIN
corpus/9eda77f2d2a9e4f99b3739c7ef8994feccbd27ed
Normal file
BIN
corpus/9eda77f2d2a9e4f99b3739c7ef8994feccbd27ed
Normal file
Binary file not shown.
BIN
corpus/9f390812bd1313d3727cf6c3502178d35f8f091d
Normal file
BIN
corpus/9f390812bd1313d3727cf6c3502178d35f8f091d
Normal file
Binary file not shown.
BIN
corpus/9f5dc619f3006591f1292fd607e6caef88a39cf6
Normal file
BIN
corpus/9f5dc619f3006591f1292fd607e6caef88a39cf6
Normal file
Binary file not shown.
BIN
corpus/a0d2aadd536eabbf2f847b8a35e7d2bec90eff43
Normal file
BIN
corpus/a0d2aadd536eabbf2f847b8a35e7d2bec90eff43
Normal file
Binary file not shown.
BIN
corpus/aa25ea759fa912e11a3f82fd4cfcd62b82e95c98
Normal file
BIN
corpus/aa25ea759fa912e11a3f82fd4cfcd62b82e95c98
Normal file
Binary file not shown.
BIN
corpus/ac3e3045ec39b92a10b517cbc600b727fcff1056
Normal file
BIN
corpus/ac3e3045ec39b92a10b517cbc600b727fcff1056
Normal file
Binary file not shown.
BIN
corpus/afab94e11f2b96751dd648bb294847901787a203
Normal file
BIN
corpus/afab94e11f2b96751dd648bb294847901787a203
Normal file
Binary file not shown.
BIN
corpus/b524307a9febdc3691c8c126c7d7fb626027dd4d
Normal file
BIN
corpus/b524307a9febdc3691c8c126c7d7fb626027dd4d
Normal file
Binary file not shown.
BIN
corpus/b6c7adfbb014456cc17d18e945c2b0a92299bece
Normal file
BIN
corpus/b6c7adfbb014456cc17d18e945c2b0a92299bece
Normal file
Binary file not shown.
BIN
corpus/b78c3ce2010ea9e0447d31be62ca126f2445f2e4
Normal file
BIN
corpus/b78c3ce2010ea9e0447d31be62ca126f2445f2e4
Normal file
Binary file not shown.
BIN
corpus/bc42dedb6dae70524792461854257d09665fc351
Normal file
BIN
corpus/bc42dedb6dae70524792461854257d09665fc351
Normal file
Binary file not shown.
BIN
corpus/bf9891838a859a00442f4cc9ea359867f576634a
Normal file
BIN
corpus/bf9891838a859a00442f4cc9ea359867f576634a
Normal file
Binary file not shown.
BIN
corpus/c4eb7f9dbf6b1b3f36a61b4f89a3ba204a793f1a
Normal file
BIN
corpus/c4eb7f9dbf6b1b3f36a61b4f89a3ba204a793f1a
Normal file
Binary file not shown.
BIN
corpus/c68b79c358de72172c32a73e8c694a379d622424
Normal file
BIN
corpus/c68b79c358de72172c32a73e8c694a379d622424
Normal file
Binary file not shown.
BIN
corpus/cca06ae98268952fd1b77f6e2abd97eb297b1a56
Normal file
BIN
corpus/cca06ae98268952fd1b77f6e2abd97eb297b1a56
Normal file
Binary file not shown.
BIN
corpus/cd7b5b4c8fc745842394a39e261490cd2123f903
Normal file
BIN
corpus/cd7b5b4c8fc745842394a39e261490cd2123f903
Normal file
Binary file not shown.
BIN
corpus/cfb0a0258f9ecdb58e1773352784543819bc947d
Normal file
BIN
corpus/cfb0a0258f9ecdb58e1773352784543819bc947d
Normal file
Binary file not shown.
BIN
corpus/d1ea327a3ee50b28de31bc8d169bacfffaaf209a
Normal file
BIN
corpus/d1ea327a3ee50b28de31bc8d169bacfffaaf209a
Normal file
Binary file not shown.
BIN
corpus/d575af201f168a680ba0a5d6a76dc02e6b130ab8
Normal file
BIN
corpus/d575af201f168a680ba0a5d6a76dc02e6b130ab8
Normal file
Binary file not shown.
BIN
corpus/d74eceff242d2281c29b65030c7e6a9844d6016c
Normal file
BIN
corpus/d74eceff242d2281c29b65030c7e6a9844d6016c
Normal file
Binary file not shown.
BIN
corpus/d7d22a5670f5ac18870856171c4555a46998bb49
Normal file
BIN
corpus/d7d22a5670f5ac18870856171c4555a46998bb49
Normal file
Binary file not shown.
BIN
corpus/d8d0c3eb0e132b0b3eefd11f1ff6660267485168
Normal file
BIN
corpus/d8d0c3eb0e132b0b3eefd11f1ff6660267485168
Normal file
Binary file not shown.
BIN
corpus/d9bb47bd8cd24b80cb11704a30f219a8a0bb3e24
Normal file
BIN
corpus/d9bb47bd8cd24b80cb11704a30f219a8a0bb3e24
Normal file
Binary file not shown.
BIN
corpus/de1f029e16846e29376769b00f3441722dcb5cf5
Normal file
BIN
corpus/de1f029e16846e29376769b00f3441722dcb5cf5
Normal file
Binary file not shown.
BIN
corpus/e4429de0453e9a6a4798c09a152e8229e4c06d56
Normal file
BIN
corpus/e4429de0453e9a6a4798c09a152e8229e4c06d56
Normal file
Binary file not shown.
BIN
corpus/e7bada9129a53f14c10b755fa6f1351d5800296b
Normal file
BIN
corpus/e7bada9129a53f14c10b755fa6f1351d5800296b
Normal file
Binary file not shown.
BIN
corpus/e7bb8ea46d657cb03bae68f19ec8727b1ad6e6bd
Normal file
BIN
corpus/e7bb8ea46d657cb03bae68f19ec8727b1ad6e6bd
Normal file
Binary file not shown.
BIN
corpus/e9d5413739467acb5d40c2ece14427cff7846196
Normal file
BIN
corpus/e9d5413739467acb5d40c2ece14427cff7846196
Normal file
Binary file not shown.
BIN
corpus/edda099a9ee07d78923e61f1db04de1ef46d2d4a
Normal file
BIN
corpus/edda099a9ee07d78923e61f1db04de1ef46d2d4a
Normal file
Binary file not shown.
BIN
corpus/f2dcdd4e3a67d7d1ca2ab43300750cb9c95fc069
Normal file
BIN
corpus/f2dcdd4e3a67d7d1ca2ab43300750cb9c95fc069
Normal file
Binary file not shown.
BIN
corpus/f5ce56ad3fcccc75a151ddb54ec1b617e33f062a
Normal file
BIN
corpus/f5ce56ad3fcccc75a151ddb54ec1b617e33f062a
Normal file
Binary file not shown.
BIN
corpus/f63507ee01e5d9c3177050bb57367996c4c62e4b
Normal file
BIN
corpus/f63507ee01e5d9c3177050bb57367996c4c62e4b
Normal file
Binary file not shown.
BIN
corpus/f77b798713563d0978096e106ba39363e53f8a28
Normal file
BIN
corpus/f77b798713563d0978096e106ba39363e53f8a28
Normal file
Binary file not shown.
BIN
corpus/fbae9c0c23d75abddc753fda7cd5d106906d889f
Normal file
BIN
corpus/fbae9c0c23d75abddc753fda7cd5d106906d889f
Normal file
Binary file not shown.
BIN
corpus/fea7852f8903597408ad333d599fcd76462ca24f
Normal file
BIN
corpus/fea7852f8903597408ad333d599fcd76462ca24f
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user