Compare commits
23 Commits
v0.0.9
...
4113183155
Author | SHA1 | Date | |
---|---|---|---|
4113183155 | |||
adb8fdc5e9 | |||
c86e407985 | |||
71a84057cb | |||
9c5e5863c2 | |||
be67555756 | |||
988ec5ce69 | |||
f5a0d81c52 | |||
3b2bd16cd1 | |||
4b3df0a426 | |||
4cdf6deb50 | |||
f21dde06d3 | |||
2b11650589 | |||
fce998460f | |||
6da3125719 | |||
79410d071f | |||
1fcca6450d | |||
55271ad06c | |||
e675612599 | |||
42b5d50492 | |||
6394995def | |||
c649bc7964 | |||
ec85a06d01 |
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
project(
|
project(
|
||||||
conflict-set
|
conflict-set
|
||||||
VERSION 0.0.9
|
VERSION 0.0.10
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
||||||
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
||||||
@@ -19,12 +19,6 @@ include(CheckCXXSourceCompiles)
|
|||||||
|
|
||||||
set(DEFAULT_BUILD_TYPE "Release")
|
set(DEFAULT_BUILD_TYPE "Release")
|
||||||
|
|
||||||
if(EMSCRIPTEN OR CMAKE_SYSTEM_NAME STREQUAL WASI)
|
|
||||||
set(WASM ON)
|
|
||||||
else()
|
|
||||||
set(WASM OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
message(
|
message(
|
||||||
STATUS
|
STATUS
|
||||||
@@ -81,15 +75,6 @@ if(EMSCRIPTEN)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT USE_SIMD_FALLBACK)
|
if(NOT USE_SIMD_FALLBACK)
|
||||||
cmake_push_check_state()
|
|
||||||
list(APPEND CMAKE_REQUIRED_FLAGS -msimd128)
|
|
||||||
check_include_file_cxx("wasm_simd128.h" HAS_WASM_SIMD)
|
|
||||||
if(HAS_WASM_SIMD)
|
|
||||||
add_compile_options(-msimd128)
|
|
||||||
add_compile_definitions(HAS_WASM_SIMD)
|
|
||||||
endif()
|
|
||||||
cmake_pop_check_state()
|
|
||||||
|
|
||||||
cmake_push_check_state()
|
cmake_push_check_state()
|
||||||
list(APPEND CMAKE_REQUIRED_FLAGS -mavx)
|
list(APPEND CMAKE_REQUIRED_FLAGS -mavx)
|
||||||
check_include_file_cxx("immintrin.h" HAS_AVX)
|
check_include_file_cxx("immintrin.h" HAS_AVX)
|
||||||
@@ -162,47 +147,40 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
|
|
||||||
file(GLOB CORPUS_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/corpus/*)
|
file(GLOB CORPUS_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/corpus/*)
|
||||||
|
|
||||||
# extra testing that relies on shared libraries, which aren't available with
|
# Shared library version of FoundationDB's skip list implementation
|
||||||
# wasm
|
add_library(skip_list SHARED SkipList.cpp)
|
||||||
if(NOT WASM)
|
target_compile_options(skip_list PRIVATE -fno-exceptions -fvisibility=hidden)
|
||||||
# Shared library version of FoundationDB's skip list implementation
|
target_include_directories(skip_list
|
||||||
add_library(skip_list SHARED SkipList.cpp)
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
target_compile_options(skip_list PRIVATE -fno-exceptions
|
set_target_properties(
|
||||||
-fvisibility=hidden)
|
skip_list PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||||
target_include_directories(skip_list
|
"${CMAKE_CURRENT_BINARY_DIR}/skip_list")
|
||||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
set_target_properties(skip_list PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||||
set_target_properties(
|
set_target_properties(skip_list PROPERTIES VERSION ${PROJECT_VERSION}
|
||||||
skip_list PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/skip_list")
|
|
||||||
set_target_properties(skip_list PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
|
||||||
set_target_properties(
|
|
||||||
skip_list PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
|
||||||
${PROJECT_VERSION_MAJOR})
|
|
||||||
|
|
||||||
# Shared library version of a std::unordered_map-based conflict set (point
|
# Shared library version of a std::unordered_map-based conflict set (point
|
||||||
# queries only)
|
# queries only)
|
||||||
add_library(hash_table SHARED HashTable.cpp)
|
add_library(hash_table SHARED HashTable.cpp)
|
||||||
target_compile_options(hash_table PRIVATE -fno-exceptions
|
target_compile_options(hash_table PRIVATE -fno-exceptions -fvisibility=hidden)
|
||||||
-fvisibility=hidden)
|
target_include_directories(hash_table
|
||||||
target_include_directories(hash_table
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
set_target_properties(
|
||||||
set_target_properties(
|
hash_table PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||||
hash_table PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
"${CMAKE_CURRENT_BINARY_DIR}/hash_table")
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/hash_table")
|
set_target_properties(hash_table PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||||
set_target_properties(hash_table PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
set_target_properties(
|
||||||
set_target_properties(
|
hash_table PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
||||||
hash_table PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
${PROJECT_VERSION_MAJOR})
|
||||||
${PROJECT_VERSION_MAJOR})
|
|
||||||
|
|
||||||
add_executable(driver_skip_list TestDriver.cpp)
|
add_executable(driver_skip_list TestDriver.cpp)
|
||||||
target_compile_options(driver_skip_list PRIVATE ${TEST_FLAGS})
|
target_compile_options(driver_skip_list PRIVATE ${TEST_FLAGS})
|
||||||
target_link_libraries(driver_skip_list PRIVATE skip_list)
|
target_link_libraries(driver_skip_list PRIVATE skip_list)
|
||||||
|
|
||||||
foreach(TEST ${CORPUS_TESTS})
|
foreach(TEST ${CORPUS_TESTS})
|
||||||
get_filename_component(hash ${TEST} NAME)
|
get_filename_component(hash ${TEST} NAME)
|
||||||
add_test(NAME skip_list_${hash} COMMAND driver_skip_list ${TEST})
|
add_test(NAME skip_list_${hash} COMMAND driver_skip_list ${TEST})
|
||||||
endforeach()
|
endforeach()
|
||||||
endif()
|
|
||||||
|
|
||||||
# ad hoc testing
|
# ad hoc testing
|
||||||
add_executable(conflict_set_main ConflictSet.cpp)
|
add_executable(conflict_set_main ConflictSet.cpp)
|
||||||
@@ -320,7 +298,7 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
add_test(NAME conflict_set_cxx_api_test COMMAND conflict_set_cxx_api_test)
|
add_test(NAME conflict_set_cxx_api_test COMMAND conflict_set_cxx_api_test)
|
||||||
|
|
||||||
# symbol visibility tests
|
# symbol visibility tests
|
||||||
if(NOT WASM AND NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
if(NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(symbol_exports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-exports.txt)
|
set(symbol_exports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-exports.txt)
|
||||||
set(symbol_imports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-imports.txt)
|
set(symbol_imports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-imports.txt)
|
||||||
|
564
ConflictSet.cpp
564
ConflictSet.cpp
@@ -18,6 +18,7 @@ limitations under the License.
|
|||||||
#include "Internal.h"
|
#include "Internal.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <compare>
|
#include <compare>
|
||||||
@@ -552,7 +553,7 @@ struct Metric {
|
|||||||
const char *name;
|
const char *name;
|
||||||
const char *help;
|
const char *help;
|
||||||
ConflictSet::MetricsV1::Type type;
|
ConflictSet::MetricsV1::Type type;
|
||||||
std::atomic<double> value;
|
std::atomic<int64_t> value;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Metric(ConflictSet::Impl *impl, const char *name, const char *help,
|
Metric(ConflictSet::Impl *impl, const char *name, const char *help,
|
||||||
@@ -563,7 +564,7 @@ struct Gauge : private Metric {
|
|||||||
Gauge(ConflictSet::Impl *impl, const char *name, const char *help)
|
Gauge(ConflictSet::Impl *impl, const char *name, const char *help)
|
||||||
: Metric(impl, name, help, ConflictSet::MetricsV1::Gauge) {}
|
: Metric(impl, name, help, ConflictSet::MetricsV1::Gauge) {}
|
||||||
|
|
||||||
void set(double value) {
|
void set(int64_t value) {
|
||||||
this->value.store(value, std::memory_order_relaxed);
|
this->value.store(value, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -573,17 +574,10 @@ struct Counter : private Metric {
|
|||||||
: Metric(impl, name, help, ConflictSet::MetricsV1::Counter) {}
|
: Metric(impl, name, help, ConflictSet::MetricsV1::Counter) {}
|
||||||
// Expensive. Accumulate locally and then call add instead of repeatedly
|
// Expensive. Accumulate locally and then call add instead of repeatedly
|
||||||
// calling add.
|
// calling add.
|
||||||
void add(double value) {
|
void add(int64_t value) {
|
||||||
assert(value >= 0);
|
assert(value >= 0);
|
||||||
static_assert(std::atomic<double>::is_always_lock_free);
|
static_assert(std::atomic<int64_t>::is_always_lock_free);
|
||||||
double old = this->value.load(std::memory_order_relaxed);
|
this->value.fetch_add(value, std::memory_order_relaxed);
|
||||||
for (;;) {
|
|
||||||
double newVal = old + value;
|
|
||||||
if (this->value.compare_exchange_weak(old, newVal,
|
|
||||||
std::memory_order_relaxed)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -708,29 +702,36 @@ size_t Node::size() const {
|
|||||||
// A type that's plumbed along the check call tree. Lifetime ends after each
|
// A type that's plumbed along the check call tree. Lifetime ends after each
|
||||||
// check call.
|
// check call.
|
||||||
struct ReadContext {
|
struct ReadContext {
|
||||||
double point_read_accum = 0;
|
int64_t point_read_accum = 0;
|
||||||
double prefix_read_accum = 0;
|
int64_t prefix_read_accum = 0;
|
||||||
double range_read_accum = 0;
|
int64_t range_read_accum = 0;
|
||||||
double point_read_short_circuit_accum = 0;
|
int64_t point_read_short_circuit_accum = 0;
|
||||||
double prefix_read_short_circuit_accum = 0;
|
int64_t prefix_read_short_circuit_accum = 0;
|
||||||
double range_read_short_circuit_accum = 0;
|
int64_t range_read_short_circuit_accum = 0;
|
||||||
double point_read_iterations_accum = 0;
|
int64_t point_read_iterations_accum = 0;
|
||||||
double prefix_read_iterations_accum = 0;
|
int64_t prefix_read_iterations_accum = 0;
|
||||||
double range_read_iterations_accum = 0;
|
int64_t range_read_iterations_accum = 0;
|
||||||
double range_read_node_scan_accum = 0;
|
int64_t range_read_node_scan_accum = 0;
|
||||||
ConflictSet::Impl *impl;
|
ConflictSet::Impl *impl;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A type that's plumbed along the non-const call tree. Same lifetime as
|
// A type that's plumbed along the non-const call tree. Same lifetime as
|
||||||
// ConflictSet::Impl
|
// ConflictSet::Impl
|
||||||
struct WriteContext {
|
struct WriteContext {
|
||||||
double entries_erased_accum = 0;
|
|
||||||
double insert_iterations_accum = 0;
|
struct Accum {
|
||||||
double entries_inserted_accum = 0;
|
int64_t entries_erased = 0;
|
||||||
double nodes_allocated_accum = 0;
|
int64_t insert_iterations = 0;
|
||||||
double nodes_released_accum = 0;
|
int64_t entries_inserted = 0;
|
||||||
|
int64_t nodes_allocated = 0;
|
||||||
|
int64_t nodes_released = 0;
|
||||||
|
int64_t point_writes = 0;
|
||||||
|
int64_t range_writes = 0;
|
||||||
|
int64_t write_bytes = 0;
|
||||||
|
} accum;
|
||||||
|
|
||||||
template <class T> T *allocate(int c) {
|
template <class T> T *allocate(int c) {
|
||||||
++nodes_allocated_accum;
|
++accum.nodes_allocated;
|
||||||
if constexpr (std::is_same_v<T, Node0>) {
|
if constexpr (std::is_same_v<T, Node0>) {
|
||||||
return node0.allocate(c);
|
return node0.allocate(c);
|
||||||
} else if constexpr (std::is_same_v<T, Node3>) {
|
} else if constexpr (std::is_same_v<T, Node3>) {
|
||||||
@@ -745,7 +746,7 @@ struct WriteContext {
|
|||||||
}
|
}
|
||||||
template <class T> void release(T *c) {
|
template <class T> void release(T *c) {
|
||||||
static_assert(!std::is_same_v<T, Node>);
|
static_assert(!std::is_same_v<T, Node>);
|
||||||
++nodes_released_accum;
|
++accum.nodes_released;
|
||||||
if constexpr (std::is_same_v<T, Node0>) {
|
if constexpr (std::is_same_v<T, Node0>) {
|
||||||
return node0.release(c);
|
return node0.release(c);
|
||||||
} else if constexpr (std::is_same_v<T, Node3>) {
|
} else if constexpr (std::is_same_v<T, Node3>) {
|
||||||
@@ -843,28 +844,41 @@ template <class NodeT> int getNodeIndex(NodeT *self, uint8_t index) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Precondition - an entry for index must exist in the node
|
||||||
|
Node *&getChildExists(Node3 *self, uint8_t index) {
|
||||||
|
return self->children[getNodeIndex(self, index)];
|
||||||
|
}
|
||||||
|
// Precondition - an entry for index must exist in the node
|
||||||
|
Node *&getChildExists(Node16 *self, uint8_t index) {
|
||||||
|
return self->children[getNodeIndex(self, index)];
|
||||||
|
}
|
||||||
|
// Precondition - an entry for index must exist in the node
|
||||||
|
Node *&getChildExists(Node48 *self, uint8_t index) {
|
||||||
|
assert(self->bitSet.test(index));
|
||||||
|
return self->children[self->index[index]];
|
||||||
|
}
|
||||||
|
// Precondition - an entry for index must exist in the node
|
||||||
|
Node *&getChildExists(Node256 *self, uint8_t index) {
|
||||||
|
assert(self->bitSet.test(index));
|
||||||
|
return self->children[index];
|
||||||
|
}
|
||||||
|
|
||||||
// Precondition - an entry for index must exist in the node
|
// Precondition - an entry for index must exist in the node
|
||||||
Node *&getChildExists(Node *self, uint8_t index) {
|
Node *&getChildExists(Node *self, uint8_t index) {
|
||||||
switch (self->getType()) {
|
switch (self->getType()) {
|
||||||
case Type_Node0: // GCOVR_EXCL_LINE
|
case Type_Node0: // GCOVR_EXCL_LINE
|
||||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
case Type_Node3: {
|
case Type_Node3: {
|
||||||
auto *self3 = static_cast<Node3 *>(self);
|
return getChildExists(static_cast<Node3 *>(self), index);
|
||||||
return self3->children[getNodeIndex(self3, index)];
|
|
||||||
}
|
}
|
||||||
case Type_Node16: {
|
case Type_Node16: {
|
||||||
auto *self16 = static_cast<Node16 *>(self);
|
return getChildExists(static_cast<Node16 *>(self), index);
|
||||||
return self16->children[getNodeIndex(self16, index)];
|
|
||||||
}
|
}
|
||||||
case Type_Node48: {
|
case Type_Node48: {
|
||||||
auto *self48 = static_cast<Node48 *>(self);
|
return getChildExists(static_cast<Node48 *>(self), index);
|
||||||
assert(self48->bitSet.test(index));
|
|
||||||
return self48->children[self48->index[index]];
|
|
||||||
}
|
}
|
||||||
case Type_Node256: {
|
case Type_Node256: {
|
||||||
auto *self256 = static_cast<Node256 *>(self);
|
return getChildExists(static_cast<Node256 *>(self), index);
|
||||||
assert(self256->bitSet.test(index));
|
|
||||||
return self256->children[index];
|
|
||||||
}
|
}
|
||||||
default: // GCOVR_EXCL_LINE
|
default: // GCOVR_EXCL_LINE
|
||||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
@@ -877,35 +891,39 @@ void setMaxVersion(Node *n, ConflictSet::Impl *, InternalVersionT maxVersion);
|
|||||||
|
|
||||||
Node *&getInTree(Node *n, ConflictSet::Impl *);
|
Node *&getInTree(Node *n, ConflictSet::Impl *);
|
||||||
|
|
||||||
|
Node *getChild(Node0 *, uint8_t) { return nullptr; }
|
||||||
|
Node *getChild(Node3 *self, uint8_t index) {
|
||||||
|
int i = getNodeIndex(self, index);
|
||||||
|
return i < 0 ? nullptr : self->children[i];
|
||||||
|
}
|
||||||
|
Node *getChild(Node16 *self, uint8_t index) {
|
||||||
|
int i = getNodeIndex(self, index);
|
||||||
|
return i < 0 ? nullptr : self->children[i];
|
||||||
|
}
|
||||||
|
Node *getChild(Node48 *self, uint8_t index) {
|
||||||
|
int i = self->index[index];
|
||||||
|
return i < 0 ? nullptr : self->children[i];
|
||||||
|
}
|
||||||
|
Node *getChild(Node256 *self, uint8_t index) { return self->children[index]; }
|
||||||
|
|
||||||
Node *getChild(Node *self, uint8_t index) {
|
Node *getChild(Node *self, uint8_t index) {
|
||||||
switch (self->getType()) {
|
switch (self->getType()) {
|
||||||
case Type_Node0:
|
case Type_Node0:
|
||||||
return nullptr;
|
return getChild(static_cast<Node0 *>(self), index);
|
||||||
case Type_Node3: {
|
case Type_Node3:
|
||||||
auto *self3 = static_cast<Node3 *>(self);
|
return getChild(static_cast<Node3 *>(self), index);
|
||||||
int i = getNodeIndex(self3, index);
|
case Type_Node16:
|
||||||
return i < 0 ? nullptr : self3->children[i];
|
return getChild(static_cast<Node16 *>(self), index);
|
||||||
}
|
case Type_Node48:
|
||||||
case Type_Node16: {
|
return getChild(static_cast<Node48 *>(self), index);
|
||||||
auto *self16 = static_cast<Node16 *>(self);
|
case Type_Node256:
|
||||||
int i = getNodeIndex(self16, index);
|
return getChild(static_cast<Node256 *>(self), index);
|
||||||
return i < 0 ? nullptr : self16->children[i];
|
|
||||||
}
|
|
||||||
case Type_Node48: {
|
|
||||||
auto *self48 = static_cast<Node48 *>(self);
|
|
||||||
int i = self48->index[index];
|
|
||||||
return i < 0 ? nullptr : self48->children[i];
|
|
||||||
}
|
|
||||||
case Type_Node256: {
|
|
||||||
auto *self256 = static_cast<Node256 *>(self);
|
|
||||||
return self256->children[index];
|
|
||||||
}
|
|
||||||
default: // GCOVR_EXCL_LINE
|
default: // GCOVR_EXCL_LINE
|
||||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class NodeT> int getChildGeqSimd(NodeT *self, int child) {
|
template <class NodeT> Node *getChildGeqSimd(NodeT *self, int child) {
|
||||||
static_assert(std::is_same_v<NodeT, Node3> || std::is_same_v<NodeT, Node16>);
|
static_assert(std::is_same_v<NodeT, Node3> || std::is_same_v<NodeT, Node16>);
|
||||||
|
|
||||||
// cachegrind says the plain loop is fewer instructions and more mis-predicted
|
// cachegrind says the plain loop is fewer instructions and more mis-predicted
|
||||||
@@ -916,10 +934,13 @@ template <class NodeT> int getChildGeqSimd(NodeT *self, int child) {
|
|||||||
Node3 *n = (Node3 *)self;
|
Node3 *n = (Node3 *)self;
|
||||||
for (int i = 0; i < n->numChildren; ++i) {
|
for (int i = 0; i < n->numChildren; ++i) {
|
||||||
if (n->index[i] >= child) {
|
if (n->index[i] >= child) {
|
||||||
return n->index[i];
|
return n->children[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (child > 255) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_AVX
|
#ifdef HAS_AVX
|
||||||
@@ -929,16 +950,7 @@ template <class NodeT> int getChildGeqSimd(NodeT *self, int child) {
|
|||||||
__m128i results = _mm_cmpeq_epi8(key_vec, _mm_min_epu8(key_vec, indices));
|
__m128i results = _mm_cmpeq_epi8(key_vec, _mm_min_epu8(key_vec, indices));
|
||||||
int mask = (1 << self->numChildren) - 1;
|
int mask = (1 << self->numChildren) - 1;
|
||||||
uint32_t bitfield = _mm_movemask_epi8(results) & mask;
|
uint32_t bitfield = _mm_movemask_epi8(results) & mask;
|
||||||
int result = bitfield == 0 ? -1 : self->index[std::countr_zero(bitfield)];
|
return bitfield == 0 ? nullptr : self->children[std::countr_zero(bitfield)];
|
||||||
assert(result == [&]() -> int {
|
|
||||||
for (int i = 0; i < self->numChildren; ++i) {
|
|
||||||
if (self->index[i] >= child) {
|
|
||||||
return self->index[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}());
|
|
||||||
return result;
|
|
||||||
#elif defined(HAS_ARM_NEON)
|
#elif defined(HAS_ARM_NEON)
|
||||||
uint8x16_t indices;
|
uint8x16_t indices;
|
||||||
memcpy(&indices, self->index, sizeof(self->index));
|
memcpy(&indices, self->index, sizeof(self->index));
|
||||||
@@ -955,47 +967,92 @@ template <class NodeT> int getChildGeqSimd(NodeT *self, int child) {
|
|||||||
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(results), 4)),
|
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(results), 4)),
|
||||||
0) &
|
0) &
|
||||||
mask;
|
mask;
|
||||||
int simd = bitfield == 0 ? -1 : self->index[std::countr_zero(bitfield) / 4];
|
return bitfield == 0 ? nullptr
|
||||||
assert(simd == [&]() -> int {
|
: self->children[std::countr_zero(bitfield) / 4];
|
||||||
for (int i = 0; i < self->numChildren; ++i) {
|
|
||||||
if (self->index[i] >= child) {
|
|
||||||
return self->index[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}());
|
|
||||||
return simd;
|
|
||||||
#else
|
#else
|
||||||
for (int i = 0; i < self->numChildren; ++i) {
|
for (int i = 0; i < self->numChildren; ++i) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
assert(self->index[i - 1] < self->index[i]);
|
assert(self->index[i - 1] < self->index[i]);
|
||||||
}
|
}
|
||||||
if (self->index[i] >= child) {
|
if (self->index[i] >= child) {
|
||||||
return self->index[i];
|
return self->children[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int getChildGeq(Node *self, int child) {
|
Node *getChildGeq(Node0 *, int) { return nullptr; }
|
||||||
if (child > 255) {
|
Node *getChildGeq(Node3 *self, int child) {
|
||||||
return -1;
|
return getChildGeqSimd(self, child);
|
||||||
|
}
|
||||||
|
Node *getChildGeq(Node16 *self, int child) {
|
||||||
|
return getChildGeqSimd(self, child);
|
||||||
|
}
|
||||||
|
Node *getChildGeq(Node48 *self, int child) {
|
||||||
|
int c = self->bitSet.firstSetGeq(child);
|
||||||
|
if (c < 0) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
return self->children[self->index[c]];
|
||||||
|
}
|
||||||
|
Node *getChildGeq(Node256 *self, int child) {
|
||||||
|
int c = self->bitSet.firstSetGeq(child);
|
||||||
|
if (c < 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return self->children[c];
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *getChildGeq(Node *self, int child) {
|
||||||
switch (self->getType()) {
|
switch (self->getType()) {
|
||||||
case Type_Node0:
|
case Type_Node0:
|
||||||
return -1;
|
return getChildGeq(static_cast<Node0 *>(self), child);
|
||||||
case Type_Node3:
|
case Type_Node3:
|
||||||
return getChildGeqSimd(static_cast<Node3 *>(self), child);
|
return getChildGeq(static_cast<Node3 *>(self), child);
|
||||||
case Type_Node16:
|
case Type_Node16:
|
||||||
return getChildGeqSimd(static_cast<Node16 *>(self), child);
|
return getChildGeq(static_cast<Node16 *>(self), child);
|
||||||
case Type_Node48:
|
case Type_Node48:
|
||||||
[[fallthrough]];
|
return getChildGeq(static_cast<Node48 *>(self), child);
|
||||||
case Type_Node256: {
|
case Type_Node256:
|
||||||
static_assert(offsetof(Node48, bitSet) == offsetof(Node256, bitSet));
|
return getChildGeq(static_cast<Node256 *>(self), child);
|
||||||
auto *self48 = static_cast<Node48 *>(self);
|
default: // GCOVR_EXCL_LINE
|
||||||
return self48->bitSet.firstSetGeq(child);
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: self has a child
|
||||||
|
Node *getFirstChildExists(Node3 *self) {
|
||||||
|
assert(self->numChildren > 0);
|
||||||
|
return self->children[0];
|
||||||
|
}
|
||||||
|
// Precondition: self has a child
|
||||||
|
Node *getFirstChildExists(Node16 *self) {
|
||||||
|
assert(self->numChildren > 0);
|
||||||
|
return self->children[0];
|
||||||
|
}
|
||||||
|
// Precondition: self has a child
|
||||||
|
Node *getFirstChildExists(Node48 *self) {
|
||||||
|
return self->children[self->index[self->bitSet.firstSetGeq(0)]];
|
||||||
|
}
|
||||||
|
// Precondition: self has a child
|
||||||
|
Node *getFirstChildExists(Node256 *self) {
|
||||||
|
return self->children[self->bitSet.firstSetGeq(0)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: self has a child
|
||||||
|
Node *getFirstChildExists(Node *self) {
|
||||||
|
switch (self->getType()) {
|
||||||
|
case Type_Node0: // GCOVR_EXCL_LINE
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
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: // GCOVR_EXCL_LINE
|
default: // GCOVR_EXCL_LINE
|
||||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
}
|
}
|
||||||
@@ -1201,8 +1258,8 @@ Node *nextPhysical(Node *node) {
|
|||||||
int index = -1;
|
int index = -1;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto nextChild = getChildGeq(node, index + 1);
|
auto nextChild = getChildGeq(node, index + 1);
|
||||||
if (nextChild >= 0) {
|
if (nextChild != nullptr) {
|
||||||
return getChildExists(node, nextChild);
|
return nextChild;
|
||||||
}
|
}
|
||||||
index = node->parentsIndex;
|
index = node->parentsIndex;
|
||||||
node = node->parent;
|
node = node->parent;
|
||||||
@@ -1458,7 +1515,7 @@ void maybeDownsize(Node *self, WriteContext *tls, ConflictSet::Impl *impl,
|
|||||||
// will update it to its new pointee as well. Precondition: `self->entryPresent`
|
// will update it to its new pointee as well. Precondition: `self->entryPresent`
|
||||||
Node *erase(Node *self, WriteContext *tls, ConflictSet::Impl *impl,
|
Node *erase(Node *self, WriteContext *tls, ConflictSet::Impl *impl,
|
||||||
bool logical, Node *&dontInvalidate) {
|
bool logical, Node *&dontInvalidate) {
|
||||||
++tls->entries_erased_accum;
|
++tls->accum.entries_erased;
|
||||||
assert(self->parent != nullptr);
|
assert(self->parent != nullptr);
|
||||||
|
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
@@ -1580,21 +1637,16 @@ Node *erase(Node *self, WriteContext *tls, ConflictSet::Impl *impl,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Iterator {
|
|
||||||
Node *n;
|
|
||||||
int cmp;
|
|
||||||
};
|
|
||||||
|
|
||||||
Node *nextSibling(Node *node) {
|
Node *nextSibling(Node *node) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (node->parent == nullptr) {
|
if (node->parent == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto next = getChildGeq(node->parent, node->parentsIndex + 1);
|
auto next = getChildGeq(node->parent, node->parentsIndex + 1);
|
||||||
if (next < 0) {
|
if (next == nullptr) {
|
||||||
node = node->parent;
|
node = node->parent;
|
||||||
} else {
|
} else {
|
||||||
return getChildExists(node->parent, next);
|
return next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1787,17 +1839,15 @@ bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
|||||||
if (n->entryPresent) {
|
if (n->entryPresent) {
|
||||||
return n->entry.pointVersion <= readVersion;
|
return n->entry.pointVersion <= readVersion;
|
||||||
}
|
}
|
||||||
int c = getChildGeq(n, 0);
|
n = getFirstChildExists(n);
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
int c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c >= 0) {
|
if (c != nullptr) {
|
||||||
n = getChildExists(n, c);
|
n = c;
|
||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
} else {
|
} else {
|
||||||
n = nextSibling(n);
|
n = nextSibling(n);
|
||||||
@@ -1837,14 +1887,9 @@ bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
downLeftSpine:
|
downLeftSpine:
|
||||||
for (;;) {
|
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||||
if (n->entryPresent) {
|
|
||||||
return n->entry.rangeVersion <= readVersion;
|
|
||||||
}
|
|
||||||
int c = getChildGeq(n, 0);
|
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
}
|
}
|
||||||
|
return n->entry.rangeVersion <= readVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logically this is the same as performing firstGeq and then checking against
|
// Logically this is the same as performing firstGeq and then checking against
|
||||||
@@ -1871,9 +1916,9 @@ bool checkPrefixRead(Node *n, const std::span<const uint8_t> key,
|
|||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
int c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c >= 0) {
|
if (c != nullptr) {
|
||||||
n = getChildExists(n, c);
|
n = c;
|
||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
} else {
|
} else {
|
||||||
n = nextSibling(n);
|
n = nextSibling(n);
|
||||||
@@ -1917,14 +1962,9 @@ bool checkPrefixRead(Node *n, const std::span<const uint8_t> key,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
downLeftSpine:
|
downLeftSpine:
|
||||||
for (;;) {
|
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||||
if (n->entryPresent) {
|
|
||||||
return n->entry.rangeVersion <= readVersion;
|
|
||||||
}
|
|
||||||
int c = getChildGeq(n, 0);
|
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
}
|
}
|
||||||
|
return n->entry.rangeVersion <= readVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_AVX
|
#ifdef HAS_AVX
|
||||||
@@ -2377,9 +2417,9 @@ bool checkRangeStartsWith(Node *n, std::span<const uint8_t> key, int begin,
|
|||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
int c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c >= 0) {
|
if (c != nullptr) {
|
||||||
n = getChildExists(n, c);
|
n = c;
|
||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
} else {
|
} else {
|
||||||
n = nextSibling(n);
|
n = nextSibling(n);
|
||||||
@@ -2423,14 +2463,9 @@ bool checkRangeStartsWith(Node *n, std::span<const uint8_t> key, int begin,
|
|||||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
|
||||||
downLeftSpine:
|
downLeftSpine:
|
||||||
for (;;) {
|
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||||
if (n->entryPresent) {
|
|
||||||
return n->entry.rangeVersion <= readVersion;
|
|
||||||
}
|
|
||||||
int c = getChildGeq(n, 0);
|
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
}
|
}
|
||||||
|
return n->entry.rangeVersion <= readVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -2478,13 +2513,13 @@ template <bool kAVX512> struct CheckRangeLeftSide {
|
|||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
int c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c >= 0) {
|
if (c != nullptr) {
|
||||||
if (searchPathLen < prefixLen) {
|
if (searchPathLen < prefixLen) {
|
||||||
n = getChildExists(n, c);
|
n = c;
|
||||||
return downLeftSpine();
|
return downLeftSpine();
|
||||||
}
|
}
|
||||||
n = getChildExists(n, c);
|
n = c;
|
||||||
ok = maxVersion(n, impl) <= readVersion;
|
ok = maxVersion(n, impl) <= readVersion;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -2543,15 +2578,10 @@ template <bool kAVX512> struct CheckRangeLeftSide {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool downLeftSpine() {
|
bool downLeftSpine() {
|
||||||
for (;;) {
|
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||||
if (n->entryPresent) {
|
|
||||||
ok = n->entry.rangeVersion <= readVersion;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int c = getChildGeq(n, 0);
|
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
}
|
}
|
||||||
|
ok = n->entry.rangeVersion <= readVersion;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2615,9 +2645,9 @@ template <bool kAVX512> struct CheckRangeRightSide {
|
|||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
int c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c >= 0) {
|
if (c != nullptr) {
|
||||||
n = getChildExists(n, c);
|
n = c;
|
||||||
return downLeftSpine();
|
return downLeftSpine();
|
||||||
} else {
|
} else {
|
||||||
return backtrack();
|
return backtrack();
|
||||||
@@ -2667,12 +2697,12 @@ template <bool kAVX512> struct CheckRangeRightSide {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto next = getChildGeq(n->parent, n->parentsIndex + 1);
|
auto next = getChildGeq(n->parent, n->parentsIndex + 1);
|
||||||
if (next < 0) {
|
if (next == nullptr) {
|
||||||
searchPathLen -= 1 + n->partialKeyLen;
|
searchPathLen -= 1 + n->partialKeyLen;
|
||||||
n = n->parent;
|
n = n->parent;
|
||||||
} else {
|
} else {
|
||||||
searchPathLen -= n->partialKeyLen;
|
searchPathLen -= n->partialKeyLen;
|
||||||
n = getChildExists(n->parent, next);
|
n = next;
|
||||||
searchPathLen += n->partialKeyLen;
|
searchPathLen += n->partialKeyLen;
|
||||||
return downLeftSpine();
|
return downLeftSpine();
|
||||||
}
|
}
|
||||||
@@ -2680,15 +2710,10 @@ template <bool kAVX512> struct CheckRangeRightSide {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool downLeftSpine() {
|
bool downLeftSpine() {
|
||||||
for (;;) {
|
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||||
if (n->entryPresent) {
|
|
||||||
ok = n->entry.rangeVersion <= readVersion;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int c = getChildGeq(n, 0);
|
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
}
|
}
|
||||||
|
ok = n->entry.rangeVersion <= readVersion;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -2838,7 +2863,7 @@ template <bool kBegin>
|
|||||||
InternalVersionT writeVersion, WriteContext *tls,
|
InternalVersionT writeVersion, WriteContext *tls,
|
||||||
ConflictSet::Impl *impl) {
|
ConflictSet::Impl *impl) {
|
||||||
|
|
||||||
for (;; ++tls->insert_iterations_accum) {
|
for (;; ++tls->accum.insert_iterations) {
|
||||||
|
|
||||||
if ((*self)->partialKeyLen > 0) {
|
if ((*self)->partialKeyLen > 0) {
|
||||||
// Handle an existing partial key
|
// Handle an existing partial key
|
||||||
@@ -2936,9 +2961,8 @@ void destroyTree(Node *root) {
|
|||||||
auto *n = toFree.back();
|
auto *n = toFree.back();
|
||||||
toFree.pop_back();
|
toFree.pop_back();
|
||||||
// Add all children to toFree
|
// Add all children to toFree
|
||||||
for (int child = getChildGeq(n, 0); child >= 0;
|
for (auto c = getChildGeq(n, 0); c != nullptr;
|
||||||
child = getChildGeq(n, child + 1)) {
|
c = getChildGeq(n, c->parentsIndex + 1)) {
|
||||||
auto *c = getChildExists(n, child);
|
|
||||||
assert(c != nullptr);
|
assert(c != nullptr);
|
||||||
toFree.push_back(c);
|
toFree.push_back(c);
|
||||||
}
|
}
|
||||||
@@ -2949,9 +2973,10 @@ void destroyTree(Node *root) {
|
|||||||
void addPointWrite(Node *&root, std::span<const uint8_t> key,
|
void addPointWrite(Node *&root, std::span<const uint8_t> key,
|
||||||
InternalVersionT writeVersion, WriteContext *tls,
|
InternalVersionT writeVersion, WriteContext *tls,
|
||||||
ConflictSet::Impl *impl) {
|
ConflictSet::Impl *impl) {
|
||||||
|
++tls->accum.point_writes;
|
||||||
auto *n = insert<true>(&root, key, writeVersion, tls, impl);
|
auto *n = insert<true>(&root, key, writeVersion, tls, impl);
|
||||||
if (!n->entryPresent) {
|
if (!n->entryPresent) {
|
||||||
++tls->entries_inserted_accum;
|
++tls->accum.entries_inserted;
|
||||||
auto *p = nextLogical(n);
|
auto *p = nextLogical(n);
|
||||||
|
|
||||||
addKey(n);
|
addKey(n);
|
||||||
@@ -2978,6 +3003,7 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
|||||||
end.back() == 0) {
|
end.back() == 0) {
|
||||||
return addPointWrite(root, begin, writeVersion, tls, impl);
|
return addPointWrite(root, begin, writeVersion, tls, impl);
|
||||||
}
|
}
|
||||||
|
++tls->accum.range_writes;
|
||||||
const bool beginIsPrefix = lcp == int(begin.size());
|
const bool beginIsPrefix = lcp == int(begin.size());
|
||||||
auto remaining = begin.subspan(0, lcp);
|
auto remaining = begin.subspan(0, lcp);
|
||||||
|
|
||||||
@@ -3021,7 +3047,7 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
|||||||
beginNode->entryPresent = true;
|
beginNode->entryPresent = true;
|
||||||
|
|
||||||
if (insertedBegin) {
|
if (insertedBegin) {
|
||||||
++tls->entries_inserted_accum;
|
++tls->accum.entries_inserted;
|
||||||
auto *p = nextLogical(beginNode);
|
auto *p = nextLogical(beginNode);
|
||||||
beginNode->entry.rangeVersion =
|
beginNode->entry.rangeVersion =
|
||||||
p == nullptr ? InternalVersionT::zero
|
p == nullptr ? InternalVersionT::zero
|
||||||
@@ -3042,7 +3068,7 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
|||||||
endNode->entryPresent = true;
|
endNode->entryPresent = true;
|
||||||
|
|
||||||
if (insertedEnd) {
|
if (insertedEnd) {
|
||||||
++tls->entries_inserted_accum;
|
++tls->accum.entries_inserted;
|
||||||
auto *p = nextLogical(endNode);
|
auto *p = nextLogical(endNode);
|
||||||
endNode->entry.pointVersion =
|
endNode->entry.pointVersion =
|
||||||
p == nullptr ? InternalVersionT::zero
|
p == nullptr ? InternalVersionT::zero
|
||||||
@@ -3074,9 +3100,9 @@ Node *firstGeqPhysical(Node *n, const std::span<const uint8_t> key) {
|
|||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
int c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c >= 0) {
|
if (c != nullptr) {
|
||||||
n = getChildExists(n, c);
|
n = c;
|
||||||
return n;
|
return n;
|
||||||
} else {
|
} else {
|
||||||
n = nextSibling(n);
|
n = nextSibling(n);
|
||||||
@@ -3118,21 +3144,15 @@ Node *firstGeqPhysical(Node *n, const std::span<const uint8_t> key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MEASURE_CHECK_CPU_TIME 0
|
|
||||||
|
|
||||||
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||||
|
|
||||||
void check(const ReadRange *reads, Result *result, int count) {
|
void check(const ReadRange *reads, Result *result, int count) {
|
||||||
#if MEASURE_CHECK_CPU_TIME
|
|
||||||
timespec ts_begin;
|
|
||||||
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts_begin);
|
|
||||||
#endif
|
|
||||||
ReadContext tls;
|
ReadContext tls;
|
||||||
tls.impl = this;
|
tls.impl = this;
|
||||||
int commits_accum = 0;
|
int commits_accum = 0;
|
||||||
int conflicts_accum = 0;
|
int conflicts_accum = 0;
|
||||||
int too_olds_accum = 0;
|
int too_olds_accum = 0;
|
||||||
double check_byte_accum = 0;
|
int64_t check_byte_accum = 0;
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
const auto &r = reads[i];
|
const auto &r = reads[i];
|
||||||
check_byte_accum += r.begin.len + r.end.len;
|
check_byte_accum += r.begin.len + r.end.len;
|
||||||
@@ -3167,13 +3187,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
conflicts_total.add(conflicts_accum);
|
conflicts_total.add(conflicts_accum);
|
||||||
too_olds_total.add(too_olds_accum);
|
too_olds_total.add(too_olds_accum);
|
||||||
check_bytes_total.add(check_byte_accum);
|
check_bytes_total.add(check_byte_accum);
|
||||||
#if MEASURE_CHECK_CPU_TIME
|
|
||||||
timespec ts_end;
|
|
||||||
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts_end);
|
|
||||||
check_cpu_seconds_total.add(
|
|
||||||
std::max<double>(0, (ts_end.tv_nsec * 1e-9 + ts_end.tv_sec) -
|
|
||||||
(ts_begin.tv_nsec * 1e-9 + ts_begin.tv_sec)));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addWrites(const WriteRange *writes, int count, int64_t writeVersion) {
|
void addWrites(const WriteRange *writes, int count, int64_t writeVersion) {
|
||||||
@@ -3184,50 +3197,51 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
|
|
||||||
assert(writeVersion >= newestVersionFullPrecision);
|
assert(writeVersion >= newestVersionFullPrecision);
|
||||||
|
|
||||||
if (writeVersion > newestVersionFullPrecision + kNominalVersionWindow) {
|
if (oldestExtantVersion < writeVersion - kMaxCorrectVersionWindow)
|
||||||
destroyTree(root);
|
[[unlikely]] {
|
||||||
init(writeVersion - kNominalVersionWindow);
|
if (writeVersion > newestVersionFullPrecision + kNominalVersionWindow) {
|
||||||
|
destroyTree(root);
|
||||||
|
init(writeVersion - kNominalVersionWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
newestVersionFullPrecision = writeVersion;
|
||||||
|
newest_version.set(newestVersionFullPrecision);
|
||||||
|
setOldestVersion(newestVersionFullPrecision - kNominalVersionWindow);
|
||||||
|
while (oldestExtantVersion <
|
||||||
|
newestVersionFullPrecision - kMaxCorrectVersionWindow) {
|
||||||
|
gcScanStep(1000);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newestVersionFullPrecision = writeVersion;
|
||||||
|
newest_version.set(newestVersionFullPrecision);
|
||||||
|
setOldestVersion(newestVersionFullPrecision - kNominalVersionWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
newestVersionFullPrecision = writeVersion;
|
|
||||||
newest_version.set(newestVersionFullPrecision);
|
|
||||||
setOldestVersion(
|
|
||||||
std::max(oldestVersionFullPrecision,
|
|
||||||
newestVersionFullPrecision - kNominalVersionWindow));
|
|
||||||
while (oldestExtantVersion <
|
|
||||||
newestVersionFullPrecision - kMaxCorrectVersionWindow) {
|
|
||||||
gcScanStep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
double write_byte_accum = 0;
|
|
||||||
int point_writes_accum = 0;
|
|
||||||
int range_writes_accum = 0;
|
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
const auto &w = writes[i];
|
const auto &w = writes[i];
|
||||||
write_byte_accum += w.begin.len + w.end.len;
|
tls.accum.write_bytes += w.begin.len + w.end.len;
|
||||||
auto begin = std::span<const uint8_t>(w.begin.p, w.begin.len);
|
auto begin = std::span<const uint8_t>(w.begin.p, w.begin.len);
|
||||||
auto end = std::span<const uint8_t>(w.end.p, w.end.len);
|
auto end = std::span<const uint8_t>(w.end.p, w.end.len);
|
||||||
if (w.end.len > 0) {
|
if (w.end.len > 0) {
|
||||||
keyUpdates += 3;
|
keyUpdates += 3;
|
||||||
++range_writes_accum;
|
|
||||||
addWriteRange(root, begin, end, InternalVersionT(writeVersion), &tls,
|
addWriteRange(root, begin, end, InternalVersionT(writeVersion), &tls,
|
||||||
this);
|
this);
|
||||||
} else {
|
} else {
|
||||||
keyUpdates += 2;
|
keyUpdates += 2;
|
||||||
++point_writes_accum;
|
|
||||||
addPointWrite(root, begin, InternalVersionT(writeVersion), &tls, this);
|
addPointWrite(root, begin, InternalVersionT(writeVersion), &tls, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_bytes.set(totalBytes);
|
memory_bytes.set(totalBytes);
|
||||||
point_writes_total.add(point_writes_accum);
|
point_writes_total.add(tls.accum.point_writes);
|
||||||
range_writes_total.add(range_writes_accum);
|
range_writes_total.add(tls.accum.range_writes);
|
||||||
nodes_allocated_total.add(std::exchange(tls.nodes_allocated_accum, 0));
|
nodes_allocated_total.add(tls.accum.nodes_allocated);
|
||||||
nodes_released_total.add(std::exchange(tls.nodes_released_accum, 0));
|
nodes_released_total.add(tls.accum.nodes_released);
|
||||||
entries_inserted_total.add(std::exchange(tls.entries_inserted_accum, 0));
|
entries_inserted_total.add(tls.accum.entries_inserted);
|
||||||
entries_erased_total.add(std::exchange(tls.entries_erased_accum, 0));
|
entries_erased_total.add(tls.accum.entries_erased);
|
||||||
insert_iterations_total.add(std::exchange(tls.insert_iterations_accum, 0));
|
insert_iterations_total.add(tls.accum.insert_iterations);
|
||||||
write_bytes_total.add(write_byte_accum);
|
write_bytes_total.add(tls.accum.write_bytes);
|
||||||
|
memset(&tls.accum, 0, sizeof(tls.accum));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spends up to `fuel` gc'ing, and returns its unused fuel. Reclaims memory
|
// Spends up to `fuel` gc'ing, and returns its unused fuel. Reclaims memory
|
||||||
@@ -3242,7 +3256,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
rootMaxVersion = std::max(rootMaxVersion, oldestVersion);
|
rootMaxVersion = std::max(rootMaxVersion, oldestVersion);
|
||||||
n = nextPhysical(n);
|
n = nextPhysical(n);
|
||||||
}
|
}
|
||||||
double set_oldest_iterations_accum = 0;
|
int64_t set_oldest_iterations_accum = 0;
|
||||||
for (; fuel > 0 && n != nullptr; ++set_oldest_iterations_accum) {
|
for (; fuel > 0 && n != nullptr; ++set_oldest_iterations_accum) {
|
||||||
rezero(n, oldestVersion);
|
rezero(n, oldestVersion);
|
||||||
// The "make sure gc keeps up with writes" calculations assume that we're
|
// The "make sure gc keeps up with writes" calculations assume that we're
|
||||||
@@ -3302,10 +3316,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
keyUpdates = gcScanStep(keyUpdates);
|
keyUpdates = gcScanStep(keyUpdates);
|
||||||
|
|
||||||
memory_bytes.set(totalBytes);
|
memory_bytes.set(totalBytes);
|
||||||
nodes_allocated_total.add(std::exchange(tls.nodes_allocated_accum, 0));
|
nodes_allocated_total.add(std::exchange(tls.accum.nodes_allocated, 0));
|
||||||
nodes_released_total.add(std::exchange(tls.nodes_released_accum, 0));
|
nodes_released_total.add(std::exchange(tls.accum.nodes_released, 0));
|
||||||
entries_inserted_total.add(std::exchange(tls.entries_inserted_accum, 0));
|
entries_inserted_total.add(std::exchange(tls.accum.entries_inserted, 0));
|
||||||
entries_erased_total.add(std::exchange(tls.entries_erased_accum, 0));
|
entries_erased_total.add(std::exchange(tls.accum.entries_erased, 0));
|
||||||
oldest_version.set(oldestVersionFullPrecision);
|
oldest_version.set(oldestVersionFullPrecision);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3443,10 +3457,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
GAUGE(
|
GAUGE(
|
||||||
oldest_extant_version,
|
oldest_extant_version,
|
||||||
"A lower bound on the lowest version associated with an existing entry");
|
"A lower bound on the lowest version associated with an existing entry");
|
||||||
#if MEASURE_CHECK_CPU_TIME
|
|
||||||
COUNTER(check_cpu_seconds_total,
|
|
||||||
"Total cpu seconds spent in a call to check");
|
|
||||||
#endif
|
|
||||||
// ==================== END METRICS DEFINITIONS ====================
|
// ==================== END METRICS DEFINITIONS ====================
|
||||||
#undef GAUGE
|
#undef GAUGE
|
||||||
#undef COUNTER
|
#undef COUNTER
|
||||||
@@ -3606,29 +3616,27 @@ double internal_getMetricValue(const ConflictSet::MetricsV1 *metric) {
|
|||||||
|
|
||||||
// GCOVR_EXCL_START
|
// GCOVR_EXCL_START
|
||||||
|
|
||||||
Iterator firstGeqLogical(Node *n, const std::span<const uint8_t> key) {
|
Node *firstGeqLogical(Node *n, const std::span<const uint8_t> key) {
|
||||||
auto remaining = key;
|
auto remaining = key;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (remaining.size() == 0) {
|
if (remaining.size() == 0) {
|
||||||
if (n->entryPresent) {
|
if (n->entryPresent) {
|
||||||
return {n, 0};
|
return n;
|
||||||
}
|
}
|
||||||
int c = getChildGeq(n, 0);
|
n = getFirstChildExists(n);
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
int c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c >= 0) {
|
if (c != nullptr) {
|
||||||
n = getChildExists(n, c);
|
n = c;
|
||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
} else {
|
} else {
|
||||||
n = nextSibling(n);
|
n = nextSibling(n);
|
||||||
if (n == nullptr) {
|
if (n == nullptr) {
|
||||||
return {nullptr, 1};
|
return nullptr;
|
||||||
}
|
}
|
||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
}
|
}
|
||||||
@@ -3660,14 +3668,9 @@ Iterator firstGeqLogical(Node *n, const std::span<const uint8_t> key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
downLeftSpine:
|
downLeftSpine:
|
||||||
for (;;) {
|
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||||
if (n->entryPresent) {
|
|
||||||
return {n, 1};
|
|
||||||
}
|
|
||||||
int c = getChildGeq(n, 0);
|
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
}
|
}
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConflictSet::check(const ReadRange *reads, Result *results,
|
void ConflictSet::check(const ReadRange *reads, Result *results,
|
||||||
@@ -3841,9 +3844,8 @@ std::string getSearchPath(Node *n) {
|
|||||||
getPartialKeyPrintable(n).c_str(), x, y);
|
getPartialKeyPrintable(n).c_str(), x, y);
|
||||||
}
|
}
|
||||||
x += kSeparation;
|
x += kSeparation;
|
||||||
for (int child = getChildGeq(n, 0); child >= 0;
|
for (auto c = getChildGeq(n, 0); c != nullptr;
|
||||||
child = getChildGeq(n, child + 1)) {
|
c = getChildGeq(n, c->parentsIndex + 1)) {
|
||||||
auto *c = getChildExists(n, child);
|
|
||||||
fprintf(file, " k_%p -> k_%p;\n", (void *)n, (void *)c);
|
fprintf(file, " k_%p -> k_%p;\n", (void *)n, (void *)c);
|
||||||
print(c, y - kSeparation);
|
print(c, y - kSeparation);
|
||||||
}
|
}
|
||||||
@@ -3862,19 +3864,19 @@ std::string getSearchPath(Node *n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void checkParentPointers(Node *node, bool &success) {
|
void checkParentPointers(Node *node, bool &success) {
|
||||||
for (int i = getChildGeq(node, 0); i >= 0; i = getChildGeq(node, i + 1)) {
|
for (auto child = getChildGeq(node, 0); child != nullptr;
|
||||||
auto *child = getChildExists(node, i);
|
child = getChildGeq(node, child->parentsIndex + 1)) {
|
||||||
if (child->parent != node) {
|
if (child->parent != node) {
|
||||||
fprintf(stderr, "%s child %d has parent pointer %p. Expected %p\n",
|
fprintf(stderr, "%s child %d has parent pointer %p. Expected %p\n",
|
||||||
getSearchPathPrintable(node).c_str(), i, (void *)child->parent,
|
getSearchPathPrintable(node).c_str(), child->parentsIndex,
|
||||||
(void *)node);
|
(void *)child->parent, (void *)node);
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
checkParentPointers(child, success);
|
checkParentPointers(child, success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator firstGeq(Node *n, std::string_view key) {
|
Node *firstGeq(Node *n, std::string_view key) {
|
||||||
return firstGeqLogical(
|
return firstGeqLogical(
|
||||||
n, std::span<const uint8_t>((const uint8_t *)key.data(), key.size()));
|
n, std::span<const uint8_t>((const uint8_t *)key.data(), key.size()));
|
||||||
}
|
}
|
||||||
@@ -3928,12 +3930,12 @@ checkMaxVersion(Node *root, Node *node, InternalVersionT oldestVersion,
|
|||||||
bool &success, ConflictSet::Impl *impl) {
|
bool &success, ConflictSet::Impl *impl) {
|
||||||
checkVersionsGeqOldestExtant(node,
|
checkVersionsGeqOldestExtant(node,
|
||||||
InternalVersionT(impl->oldestExtantVersion));
|
InternalVersionT(impl->oldestExtantVersion));
|
||||||
auto expected = InternalVersionT::zero;
|
auto expected = oldestVersion;
|
||||||
if (node->entryPresent) {
|
if (node->entryPresent) {
|
||||||
expected = std::max(expected, node->entry.pointVersion);
|
expected = std::max(expected, node->entry.pointVersion);
|
||||||
}
|
}
|
||||||
for (int i = getChildGeq(node, 0); i >= 0; i = getChildGeq(node, i + 1)) {
|
for (auto child = getChildGeq(node, 0); child != nullptr;
|
||||||
auto *child = getChildExists(node, i);
|
child = getChildGeq(node, child->parentsIndex + 1)) {
|
||||||
expected = std::max(
|
expected = std::max(
|
||||||
expected, checkMaxVersion(root, child, oldestVersion, success, impl));
|
expected, checkMaxVersion(root, child, oldestVersion, success, impl));
|
||||||
if (child->entryPresent) {
|
if (child->entryPresent) {
|
||||||
@@ -3945,8 +3947,8 @@ checkMaxVersion(Node *root, Node *node, InternalVersionT oldestVersion,
|
|||||||
auto inc = strinc(key, ok);
|
auto inc = strinc(key, ok);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
auto borrowed = firstGeq(root, inc);
|
auto borrowed = firstGeq(root, inc);
|
||||||
if (borrowed.n != nullptr) {
|
if (borrowed != nullptr) {
|
||||||
expected = std::max(expected, borrowed.n->entry.rangeVersion);
|
expected = std::max(expected, borrowed->entry.rangeVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (maxVersion(node, impl) > oldestVersion &&
|
if (maxVersion(node, impl) > oldestVersion &&
|
||||||
@@ -3961,14 +3963,14 @@ checkMaxVersion(Node *root, Node *node, InternalVersionT oldestVersion,
|
|||||||
|
|
||||||
[[maybe_unused]] int64_t checkEntriesExist(Node *node, bool &success) {
|
[[maybe_unused]] int64_t checkEntriesExist(Node *node, bool &success) {
|
||||||
int64_t total = node->entryPresent;
|
int64_t total = node->entryPresent;
|
||||||
for (int i = getChildGeq(node, 0); i >= 0; i = getChildGeq(node, i + 1)) {
|
for (auto child = getChildGeq(node, 0); child != nullptr;
|
||||||
auto *child = getChildExists(node, i);
|
child = getChildGeq(node, child->parentsIndex + 1)) {
|
||||||
int64_t e = checkEntriesExist(child, success);
|
int64_t e = checkEntriesExist(child, success);
|
||||||
total += e;
|
total += e;
|
||||||
if (e == 0) {
|
if (e == 0) {
|
||||||
Arena arena;
|
Arena arena;
|
||||||
fprintf(stderr, "%s has child %02x with no reachable entries\n",
|
fprintf(stderr, "%s has child %02x with no reachable entries\n",
|
||||||
getSearchPathPrintable(node).c_str(), i);
|
getSearchPathPrintable(node).c_str(), child->parentsIndex);
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4005,8 +4007,8 @@ checkMaxVersion(Node *root, Node *node, InternalVersionT oldestVersion,
|
|||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
// TODO check that the max capacity property eventually holds
|
// TODO check that the max capacity property eventually holds
|
||||||
for (int i = getChildGeq(node, 0); i >= 0; i = getChildGeq(node, i + 1)) {
|
for (auto child = getChildGeq(node, 0); child != nullptr;
|
||||||
auto *child = getChildExists(node, i);
|
child = getChildGeq(node, child->parentsIndex + 1)) {
|
||||||
checkMemoryBoundInvariants(child, success);
|
checkMemoryBoundInvariants(child, success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4151,26 +4153,42 @@ int main(void) { printTree(); }
|
|||||||
|
|
||||||
#ifdef ENABLE_FUZZ
|
#ifdef ENABLE_FUZZ
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
TestDriver<ConflictSet::Impl> driver{data, size};
|
Arbitrary arbitrary({data, size});
|
||||||
|
TestDriver<ConflictSet::Impl> driver1{arbitrary};
|
||||||
|
TestDriver<ConflictSet::Impl> driver2{arbitrary};
|
||||||
|
|
||||||
|
bool done1 = false;
|
||||||
|
bool done2 = false;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
bool done = driver.next();
|
if (!done1) {
|
||||||
if (!driver.ok) {
|
done1 = driver1.next();
|
||||||
debugPrintDot(stdout, driver.cs.root, &driver.cs);
|
if (!driver1.ok) {
|
||||||
fflush(stdout);
|
debugPrintDot(stdout, driver1.cs.root, &driver1.cs);
|
||||||
abort();
|
fflush(stdout);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!checkCorrectness(driver1.cs.root, driver1.cs.oldestVersion,
|
||||||
|
&driver1.cs)) {
|
||||||
|
debugPrintDot(stdout, driver1.cs.root, &driver1.cs);
|
||||||
|
fflush(stdout);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
if (!done2) {
|
||||||
fprintf(stderr, "Check correctness\n");
|
done2 = driver2.next();
|
||||||
#endif
|
if (!driver2.ok) {
|
||||||
bool success =
|
debugPrintDot(stdout, driver2.cs.root, &driver2.cs);
|
||||||
checkCorrectness(driver.cs.root, driver.cs.oldestVersion, &driver.cs);
|
fflush(stdout);
|
||||||
if (!success) {
|
abort();
|
||||||
debugPrintDot(stdout, driver.cs.root, &driver.cs);
|
}
|
||||||
fflush(stdout);
|
if (!checkCorrectness(driver2.cs.root, driver2.cs.oldestVersion,
|
||||||
abort();
|
&driver2.cs)) {
|
||||||
|
debugPrintDot(stdout, driver2.cs.root, &driver2.cs);
|
||||||
|
fflush(stdout);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (done) {
|
if (done1 && done2) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
126
Internal.h
126
Internal.h
@@ -580,11 +580,14 @@ namespace {
|
|||||||
|
|
||||||
template <class ConflictSetImpl, bool kEnableAssertions = true>
|
template <class ConflictSetImpl, bool kEnableAssertions = true>
|
||||||
struct TestDriver {
|
struct TestDriver {
|
||||||
Arbitrary arbitrary;
|
Arbitrary *arbitrary;
|
||||||
explicit TestDriver(const uint8_t *data, size_t size)
|
explicit TestDriver(Arbitrary &a) : arbitrary(&a) {
|
||||||
: arbitrary({data, size}) {}
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
fprintf(stderr, "%p Initial version: {%" PRId64 "}\n", this, writeVersion);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int64_t oldestVersion = arbitrary.next();
|
int64_t oldestVersion = arbitrary->next();
|
||||||
int64_t writeVersion = oldestVersion;
|
int64_t writeVersion = oldestVersion;
|
||||||
ConflictSetImpl cs{oldestVersion};
|
ConflictSetImpl cs{oldestVersion};
|
||||||
ReferenceImpl refImpl{oldestVersion};
|
ReferenceImpl refImpl{oldestVersion};
|
||||||
@@ -593,33 +596,34 @@ struct TestDriver {
|
|||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
|
||||||
const int prefixLen = arbitrary.bounded(512);
|
const int prefixLen = arbitrary->bounded(512);
|
||||||
const int prefixByte = arbitrary.randT<uint8_t>();
|
const int prefixByte = arbitrary->randT<uint8_t>();
|
||||||
|
|
||||||
// Call until it returns true, for "done". Check internal invariants etc
|
// Call until it returns true, for "done". Check internal invariants etc
|
||||||
// between calls to next.
|
// between calls to next.
|
||||||
bool next() {
|
bool next() {
|
||||||
assert(cs.getBytes() >= 0);
|
assert(cs.getBytes() >= 0);
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary->hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Arena arena;
|
Arena arena;
|
||||||
{
|
{
|
||||||
int numPointWrites = arbitrary.bounded(100);
|
int numPointWrites = arbitrary->bounded(100);
|
||||||
int numRangeWrites = arbitrary.bounded(100);
|
int numRangeWrites = arbitrary->bounded(100);
|
||||||
int64_t v = (writeVersion += arbitrary.bounded(10) ? arbitrary.bounded(10)
|
int64_t v =
|
||||||
: arbitrary.next());
|
(writeVersion +=
|
||||||
|
arbitrary->bounded(10) ? arbitrary->bounded(10) : arbitrary->next());
|
||||||
auto *writes =
|
auto *writes =
|
||||||
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
||||||
auto keys = set<std::string_view>(arena);
|
auto keys = set<std::string_view>(arena);
|
||||||
while (int(keys.size()) < numPointWrites + numRangeWrites * 2) {
|
while (int(keys.size()) < numPointWrites + numRangeWrites * 2) {
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary->hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = prefixLen + arbitrary->bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
arbitrary->randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
keys.insert(std::string_view((const char *)begin, keyLen));
|
keys.insert(std::string_view((const char *)begin, keyLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,7 +633,7 @@ struct TestDriver {
|
|||||||
rangesRemaining = numRangeWrites;
|
rangesRemaining = numRangeWrites;
|
||||||
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
||||||
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
||||||
? bool(arbitrary.bounded(2))
|
? bool(arbitrary->bounded(2))
|
||||||
: pointsRemaining > 0;
|
: pointsRemaining > 0;
|
||||||
if (pointRead) {
|
if (pointRead) {
|
||||||
assert(pointsRemaining > 0);
|
assert(pointsRemaining > 0);
|
||||||
@@ -648,33 +652,20 @@ struct TestDriver {
|
|||||||
++iter;
|
++iter;
|
||||||
--rangesRemaining;
|
--rangesRemaining;
|
||||||
}
|
}
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
|
||||||
if (writes[i].end.len == 0) {
|
|
||||||
fprintf(stderr, "Write: {%s}\n", printable(writes[i].begin).c_str());
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Write: [%s, %s)\n",
|
|
||||||
printable(writes[i].begin).c_str(),
|
|
||||||
printable(writes[i].end).c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
assert(iter == keys.end());
|
assert(iter == keys.end());
|
||||||
assert(i == numPointWrites + numRangeWrites);
|
assert(i == numPointWrites + numRangeWrites);
|
||||||
|
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
|
||||||
fprintf(stderr, "Write @ %" PRId64 "\n", v);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Test non-canonical writes
|
// Test non-canonical writes
|
||||||
if (numPointWrites > 0) {
|
if (numPointWrites > 0) {
|
||||||
int overlaps = arbitrary.bounded(numPointWrites);
|
int overlaps = arbitrary->bounded(numPointWrites);
|
||||||
for (int i = 0; i < numPointWrites + numRangeWrites && overlaps > 0;
|
for (int i = 0; i < numPointWrites + numRangeWrites && overlaps > 0;
|
||||||
++i) {
|
++i) {
|
||||||
if (writes[i].end.len == 0) {
|
if (writes[i].end.len == 0) {
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = prefixLen + arbitrary->bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
arbitrary->randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
writes[i].end.len = keyLen;
|
writes[i].end.len = keyLen;
|
||||||
writes[i].end.p = begin;
|
writes[i].end.p = begin;
|
||||||
auto c =
|
auto c =
|
||||||
@@ -692,10 +683,10 @@ struct TestDriver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (arbitrary.bounded(2)) {
|
if (arbitrary->bounded(2)) {
|
||||||
// Shuffle writes
|
// Shuffle writes
|
||||||
for (int i = numPointWrites + numRangeWrites - 1; i > 0; --i) {
|
for (int i = numPointWrites + numRangeWrites - 1; i > 0; --i) {
|
||||||
int j = arbitrary.bounded(i + 1);
|
int j = arbitrary->bounded(i + 1);
|
||||||
if (i != j) {
|
if (i != j) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(writes[i], writes[j]);
|
swap(writes[i], writes[j]);
|
||||||
@@ -704,7 +695,7 @@ struct TestDriver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldestVersion +=
|
oldestVersion +=
|
||||||
arbitrary.bounded(10) ? arbitrary.bounded(10) : arbitrary.next();
|
arbitrary->bounded(10) ? arbitrary->bounded(10) : arbitrary->next();
|
||||||
oldestVersion = std::min(oldestVersion, writeVersion);
|
oldestVersion = std::min(oldestVersion, writeVersion);
|
||||||
|
|
||||||
#ifdef THREAD_TEST
|
#ifdef THREAD_TEST
|
||||||
@@ -721,6 +712,20 @@ struct TestDriver {
|
|||||||
ready.wait();
|
ready.wait();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
for (int i = 0; i < numPointWrites + numRangeWrites; ++i) {
|
||||||
|
if (writes[i].end.len == 0) {
|
||||||
|
fprintf(stderr, "%p Write: {%s}\n", this,
|
||||||
|
printable(writes[i].begin).c_str());
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%p Write: [%s, %s)\n", this,
|
||||||
|
printable(writes[i].begin).c_str(),
|
||||||
|
printable(writes[i].end).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%p Write @ %" PRId64 "\n", this, v);
|
||||||
|
#endif
|
||||||
|
|
||||||
CALLGRIND_START_INSTRUMENTATION;
|
CALLGRIND_START_INSTRUMENTATION;
|
||||||
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
CALLGRIND_STOP_INSTRUMENTATION;
|
CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
@@ -729,6 +734,10 @@ struct TestDriver {
|
|||||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
fprintf(stderr, "%p Set oldest version: %" PRId64 "\n", this,
|
||||||
|
oldestVersion);
|
||||||
|
#endif
|
||||||
cs.setOldestVersion(oldestVersion);
|
cs.setOldestVersion(oldestVersion);
|
||||||
if constexpr (kEnableAssertions) {
|
if constexpr (kEnableAssertions) {
|
||||||
refImpl.setOldestVersion(oldestVersion);
|
refImpl.setOldestVersion(oldestVersion);
|
||||||
@@ -739,24 +748,24 @@ struct TestDriver {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int numPointReads = arbitrary.bounded(100);
|
int numPointReads = arbitrary->bounded(100);
|
||||||
int numRangeReads = arbitrary.bounded(100);
|
int numRangeReads = arbitrary->bounded(100);
|
||||||
|
|
||||||
int64_t v = std::max<int64_t>(writeVersion - (arbitrary.bounded(10)
|
int64_t v = std::max<int64_t>(writeVersion - (arbitrary->bounded(10)
|
||||||
? arbitrary.bounded(10)
|
? arbitrary->bounded(10)
|
||||||
: arbitrary.next()),
|
: arbitrary->next()),
|
||||||
0);
|
0);
|
||||||
auto *reads =
|
auto *reads =
|
||||||
new (arena) ConflictSet::ReadRange[numPointReads + numRangeReads];
|
new (arena) ConflictSet::ReadRange[numPointReads + numRangeReads];
|
||||||
auto keys = set<std::string_view>(arena);
|
auto keys = set<std::string_view>(arena);
|
||||||
while (int(keys.size()) < numPointReads + numRangeReads * 2) {
|
while (int(keys.size()) < numPointReads + numRangeReads * 2) {
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary->hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = prefixLen + arbitrary->bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
arbitrary->randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
keys.insert(std::string_view((const char *)begin, keyLen));
|
keys.insert(std::string_view((const char *)begin, keyLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -765,7 +774,7 @@ struct TestDriver {
|
|||||||
for (int pointsRemaining = numPointReads, rangesRemaining = numRangeReads;
|
for (int pointsRemaining = numPointReads, rangesRemaining = numRangeReads;
|
||||||
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
||||||
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
||||||
? bool(arbitrary.bounded(2))
|
? bool(arbitrary->bounded(2))
|
||||||
: pointsRemaining > 0;
|
: pointsRemaining > 0;
|
||||||
if (pointRead) {
|
if (pointRead) {
|
||||||
assert(pointsRemaining > 0);
|
assert(pointsRemaining > 0);
|
||||||
@@ -787,10 +796,10 @@ struct TestDriver {
|
|||||||
reads[i].readVersion = v;
|
reads[i].readVersion = v;
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
if (reads[i].end.len == 0) {
|
if (reads[i].end.len == 0) {
|
||||||
fprintf(stderr, "Read: {%s} @ %" PRId64 "\n",
|
fprintf(stderr, "%p Read: {%s} @ %" PRId64 "\n", this,
|
||||||
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Read: [%s, %s) @ %" PRId64 "\n",
|
fprintf(stderr, "%p Read: [%s, %s) @ %" PRId64 "\n", this,
|
||||||
printable(reads[i].begin).c_str(),
|
printable(reads[i].begin).c_str(),
|
||||||
printable(reads[i].end).c_str(), reads[i].readVersion);
|
printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
}
|
}
|
||||||
@@ -839,24 +848,27 @@ struct TestDriver {
|
|||||||
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto compareResults = [reads](ConflictSet::Result *results1,
|
auto compareResults = [reads, this](ConflictSet::Result *results1,
|
||||||
ConflictSet::Result *results2, int count) {
|
ConflictSet::Result *results2,
|
||||||
|
int count) {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
if (results1[i] != results2[i]) {
|
if (results1[i] != results2[i]) {
|
||||||
if (reads[i].end.len == 0) {
|
if (reads[i].end.len == 0) {
|
||||||
fprintf(stderr,
|
|
||||||
"Expected %s, got %s for read of {%s} at version %" PRId64
|
|
||||||
"\n",
|
|
||||||
resultToStr(results2[i]), resultToStr(results1[i]),
|
|
||||||
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
|
||||||
} else {
|
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"Expected %s, got %s for read of [%s, %s) at version %" PRId64
|
"%p Expected %s, got %s for read of {%s} at version %" PRId64
|
||||||
"\n",
|
"\n",
|
||||||
resultToStr(results2[i]), resultToStr(results1[i]),
|
(void *)this, resultToStr(results2[i]),
|
||||||
printable(reads[i].begin).c_str(),
|
resultToStr(results1[i]), printable(reads[i].begin).c_str(),
|
||||||
printable(reads[i].end).c_str(), reads[i].readVersion);
|
reads[i].readVersion);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%p Expected %s, got %s for read of [%s, %s) at version "
|
||||||
|
"%" PRId64 "\n",
|
||||||
|
(void *)this, resultToStr(results2[i]),
|
||||||
|
resultToStr(results1[i]),
|
||||||
|
printable(reads[i].begin).c_str(),
|
||||||
|
printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -778,10 +778,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
for (int s = stripes - 1; s >= 0; s--) {
|
for (int s = stripes - 1; s >= 0; s--) {
|
||||||
for (int i = 0; i * 2 < ss; ++i) {
|
for (int i = 0; i * 2 < ss; ++i) {
|
||||||
const auto &w = combinedWriteConflictRanges[s * stripeSize / 2 + i];
|
const auto &w = combinedWriteConflictRanges[s * stripeSize / 2 + i];
|
||||||
#if DEBUG_VERBOSE
|
|
||||||
printf("Write begin: %s\n", printable(w.begin).c_str());
|
|
||||||
fflush(stdout);
|
|
||||||
#endif
|
|
||||||
values[i * 2] = w.first;
|
values[i * 2] = w.first;
|
||||||
values[i * 2 + 1] = w.second;
|
values[i * 2 + 1] = w.second;
|
||||||
keyUpdates += 3;
|
keyUpdates += 3;
|
||||||
|
@@ -13,25 +13,58 @@ int main(int argc, char **argv) {
|
|||||||
std::stringstream buffer;
|
std::stringstream buffer;
|
||||||
buffer << t.rdbuf();
|
buffer << t.rdbuf();
|
||||||
auto str = buffer.str();
|
auto str = buffer.str();
|
||||||
TestDriver<ConflictSet, !PERF_TEST> driver{(const uint8_t *)str.data(),
|
Arbitrary arbitrary({(const uint8_t *)str.data(), str.size()});
|
||||||
str.size()};
|
TestDriver<ConflictSet, !PERF_TEST> driver1{arbitrary};
|
||||||
while (!driver.next())
|
TestDriver<ConflictSet, !PERF_TEST> driver2{arbitrary};
|
||||||
;
|
bool done1 = false;
|
||||||
if (!driver.ok) {
|
bool done2 = false;
|
||||||
abort();
|
for (;;) {
|
||||||
|
if (!done1) {
|
||||||
|
done1 = driver1.next();
|
||||||
|
if (!driver1.ok) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!done2) {
|
||||||
|
done2 = driver2.next();
|
||||||
|
if (!driver2.ok) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (done1 && done2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConflictSet::MetricsV1 *metrics;
|
{
|
||||||
int metricsCount;
|
ConflictSet::MetricsV1 *metrics;
|
||||||
driver.cs.getMetricsV1(&metrics, &metricsCount);
|
int metricsCount;
|
||||||
printf("#################### METRICS FOR %s ####################\n",
|
driver1.cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
argv[i]);
|
printf("#################### METRICS for ConflictSet 1 for %s "
|
||||||
for (int i = 0; i < metricsCount; ++i) {
|
"####################\n",
|
||||||
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
argv[i]);
|
||||||
printf("# TYPE %s %s\n", metrics[i].name,
|
for (int i = 0; i < metricsCount; ++i) {
|
||||||
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||||
printf("%s %g\n", metrics[i].name, metrics[i].getValue());
|
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());
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ConflictSet::MetricsV1 *metrics;
|
||||||
|
int metricsCount;
|
||||||
|
driver2.cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
|
printf("#################### METRICS for ConflictSet 2 for %s "
|
||||||
|
"####################\n",
|
||||||
|
argv[i]);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
}
|
}
|
||||||
puts("");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
__aarch64_cas8_relax
|
__aarch64_ldadd8_relax
|
||||||
__getauxval@GLIBC_2.17
|
__getauxval@GLIBC_2.17
|
||||||
__stack_chk_fail@GLIBC_2.17
|
__stack_chk_fail@GLIBC_2.17
|
||||||
__stack_chk_guard@GLIBC_2.17
|
__stack_chk_guard@GLIBC_2.17
|
||||||
|
BIN
corpus/004a3d124875e031f4c28421b43c131abda8f376
Normal file
BIN
corpus/004a3d124875e031f4c28421b43c131abda8f376
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0092e9f642b6f4d2cc1f3e6a16bf59624107aa1d
Normal file
BIN
corpus/0092e9f642b6f4d2cc1f3e6a16bf59624107aa1d
Normal file
Binary file not shown.
BIN
corpus/0112c3723c9ab42395c0f74e446998c0c63e8aef
Normal file
BIN
corpus/0112c3723c9ab42395c0f74e446998c0c63e8aef
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/01a8d6a44f22aa06b638a795f8a2dffd1ba9b0a8
Normal file
BIN
corpus/01a8d6a44f22aa06b638a795f8a2dffd1ba9b0a8
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/01ce88737ddc4d23f78bd6fe13dfbb5e1c5ffb18
Normal file
BIN
corpus/01ce88737ddc4d23f78bd6fe13dfbb5e1c5ffb18
Normal file
Binary file not shown.
BIN
corpus/021f64c6c477dc26894e5bc701554b1187fd3d0b
Normal file
BIN
corpus/021f64c6c477dc26894e5bc701554b1187fd3d0b
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/02fa9006f44b0d14655ffc31635a1a9df302225f
Normal file
BIN
corpus/02fa9006f44b0d14655ffc31635a1a9df302225f
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/04113be6af30d9ce06aa1da99259f797dee4b827
Normal file
BIN
corpus/04113be6af30d9ce06aa1da99259f797dee4b827
Normal file
Binary file not shown.
BIN
corpus/046d2c5bf09695809fe03ce5e8650ee03c86e767
Normal file
BIN
corpus/046d2c5bf09695809fe03ce5e8650ee03c86e767
Normal file
Binary file not shown.
BIN
corpus/048559df8c0ed2fdf38a177d7fe1afb40f3d034d
Normal file
BIN
corpus/048559df8c0ed2fdf38a177d7fe1afb40f3d034d
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/04cf08496c406ee4442f596a87000a1768cd0a94
Normal file
BIN
corpus/04cf08496c406ee4442f596a87000a1768cd0a94
Normal file
Binary file not shown.
BIN
corpus/06a3915af45c58d5edad76f7192d00dc71aefdc3
Normal file
BIN
corpus/06a3915af45c58d5edad76f7192d00dc71aefdc3
Normal file
Binary file not shown.
BIN
corpus/06dcf68e4bdbe913d3b272e3d4b1dc6f77309f2d
Normal file
BIN
corpus/06dcf68e4bdbe913d3b272e3d4b1dc6f77309f2d
Normal file
Binary file not shown.
BIN
corpus/06e7021fcbe50e9fd239738577bc87e54ac491b2
Normal file
BIN
corpus/06e7021fcbe50e9fd239738577bc87e54ac491b2
Normal file
Binary file not shown.
BIN
corpus/076402dbe642f66076a44d56c79034eeec433142
Normal file
BIN
corpus/076402dbe642f66076a44d56c79034eeec433142
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/08a5364e7da8be32f3dbffcb5ad9fc636cdfb461
Normal file
BIN
corpus/08a5364e7da8be32f3dbffcb5ad9fc636cdfb461
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/09f7205079111282e06b972b7b0148077c780a15
Normal file
BIN
corpus/09f7205079111282e06b972b7b0148077c780a15
Normal file
Binary file not shown.
BIN
corpus/0a95ed480d47305499128ff09afba6bbdbec5a5c
Normal file
BIN
corpus/0a95ed480d47305499128ff09afba6bbdbec5a5c
Normal file
Binary file not shown.
BIN
corpus/0ab276238fb98a371fafe99e7c98558c07baef7e
Normal file
BIN
corpus/0ab276238fb98a371fafe99e7c98558c07baef7e
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0b471bdfb0cf6af786adf2cf8bc3fc1bae62860c
Normal file
BIN
corpus/0b471bdfb0cf6af786adf2cf8bc3fc1bae62860c
Normal file
Binary file not shown.
BIN
corpus/0b4ba4b5f7cc077a6f0ef5d48bcea80118ef7aa2
Normal file
BIN
corpus/0b4ba4b5f7cc077a6f0ef5d48bcea80118ef7aa2
Normal file
Binary file not shown.
BIN
corpus/0b4c470b5893ca01dd32e15b8082d48f48617fd1
Normal file
BIN
corpus/0b4c470b5893ca01dd32e15b8082d48f48617fd1
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0b741f462204a40a55522bc307fcafa4099530cc
Normal file
BIN
corpus/0b741f462204a40a55522bc307fcafa4099530cc
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0b90760335f1b17bc108bcff27a95ad88231d5e4
Normal file
BIN
corpus/0b90760335f1b17bc108bcff27a95ad88231d5e4
Normal file
Binary file not shown.
BIN
corpus/0ba4f637960e9e2696f9415be7bc6c5759bf927e
Normal file
BIN
corpus/0ba4f637960e9e2696f9415be7bc6c5759bf927e
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0c93df22a52831a77be1e65a1d3c56d80825940b
Normal file
BIN
corpus/0c93df22a52831a77be1e65a1d3c56d80825940b
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0d99bee638044b26e13b57a0db5ee82740dfd312
Normal file
BIN
corpus/0d99bee638044b26e13b57a0db5ee82740dfd312
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0e1e4a2e118140a221e4bbc9251059c7a0e34f49
Normal file
BIN
corpus/0e1e4a2e118140a221e4bbc9251059c7a0e34f49
Normal file
Binary file not shown.
BIN
corpus/0ed829974af554e6c3b5ed9fb845333ccf5d8c1d
Normal file
BIN
corpus/0ed829974af554e6c3b5ed9fb845333ccf5d8c1d
Normal file
Binary file not shown.
BIN
corpus/0eebea0577f503e082c47691fec2651467eeaf5f
Normal file
BIN
corpus/0eebea0577f503e082c47691fec2651467eeaf5f
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5
corpus/0f5c5f28c31e44ee0ec085fec09adf0981065771
Normal file
5
corpus/0f5c5f28c31e44ee0ec085fec09adf0981065771
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
<EFBFBD><EFBFBD>
|
||||||
|
|
||||||
|
|
||||||
|
2
|
Binary file not shown.
BIN
corpus/104fd2d945317d09b2c44ea57e9e333f49115c76
Normal file
BIN
corpus/104fd2d945317d09b2c44ea57e9e333f49115c76
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/123d84d187ef92218bcb439255db737cae375151
Normal file
BIN
corpus/123d84d187ef92218bcb439255db737cae375151
Normal file
Binary file not shown.
BIN
corpus/129a26abd16441eb995a2fad52fbce02112e4d20
Normal file
BIN
corpus/129a26abd16441eb995a2fad52fbce02112e4d20
Normal file
Binary file not shown.
Binary file not shown.
1
corpus/12bdd00fd4038756cbcf8ecdad1b0cd862603cd8
Normal file
1
corpus/12bdd00fd4038756cbcf8ecdad1b0cd862603cd8
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<EFBFBD>
|
BIN
corpus/12cea490706f122949fa348f9de2a3ace31d56d2
Normal file
BIN
corpus/12cea490706f122949fa348f9de2a3ace31d56d2
Normal file
Binary file not shown.
BIN
corpus/12ecc1deca18dccdf8459fe63905bf51ced8b776
Normal file
BIN
corpus/12ecc1deca18dccdf8459fe63905bf51ced8b776
Normal file
Binary file not shown.
BIN
corpus/131c2ff7d2742dc4f468d6c87c4e3964a95db144
Normal file
BIN
corpus/131c2ff7d2742dc4f468d6c87c4e3964a95db144
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1363c70a6cb83cf8f0d79c316449b7b59aebf68d
Normal file
BIN
corpus/1363c70a6cb83cf8f0d79c316449b7b59aebf68d
Normal file
Binary file not shown.
BIN
corpus/147776bc8ae88fe97d2d51a00d5868a981cdd44d
Normal file
BIN
corpus/147776bc8ae88fe97d2d51a00d5868a981cdd44d
Normal file
Binary file not shown.
BIN
corpus/1478958bdc54001cb65507dc6b98bfd35d00c3b2
Normal file
BIN
corpus/1478958bdc54001cb65507dc6b98bfd35d00c3b2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/159a974ac30ebe83f44dea1d43b6cdd45bcb56b3
Normal file
BIN
corpus/159a974ac30ebe83f44dea1d43b6cdd45bcb56b3
Normal file
Binary file not shown.
BIN
corpus/16638187a55c2c126a8b6f46cbb708e83573c1ea
Normal file
BIN
corpus/16638187a55c2c126a8b6f46cbb708e83573c1ea
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/169a539673da8f98fd8a6941b36bdb80622e5d68
Normal file
BIN
corpus/169a539673da8f98fd8a6941b36bdb80622e5d68
Normal file
Binary file not shown.
BIN
corpus/16bae93ce7f55257f650cc0e095b613191e07e0c
Normal file
BIN
corpus/16bae93ce7f55257f650cc0e095b613191e07e0c
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/18e24277a0bc2a47e9262fd2282ca68f5aa5c09b
Normal file
BIN
corpus/18e24277a0bc2a47e9262fd2282ca68f5aa5c09b
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/19149503d26fa7a5add35147a8b77dd436213003
Normal file
BIN
corpus/19149503d26fa7a5add35147a8b77dd436213003
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1a9cbbcb6e0d6ea624ac16e0e72a420056f60885
Normal file
BIN
corpus/1a9cbbcb6e0d6ea624ac16e0e72a420056f60885
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1bdd11c6a929e0695d33db2b25136d11b786747a
Normal file
BIN
corpus/1bdd11c6a929e0695d33db2b25136d11b786747a
Normal file
Binary file not shown.
BIN
corpus/1be0b3f143ffa33fae34725306c6c07d6175e01a
Normal file
BIN
corpus/1be0b3f143ffa33fae34725306c6c07d6175e01a
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1c3b103b6b47a392401f1354f2fd53f882e81541
Normal file
BIN
corpus/1c3b103b6b47a392401f1354f2fd53f882e81541
Normal file
Binary file not shown.
BIN
corpus/1c999c8cb335e3cc099a6220bab1a43d69164540
Normal file
BIN
corpus/1c999c8cb335e3cc099a6220bab1a43d69164540
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1d41b59576afaad2b0e6171aa048e13c5600a558
Normal file
BIN
corpus/1d41b59576afaad2b0e6171aa048e13c5600a558
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1dace1035ef72ca4dd3f0c9c217634e2ac837c48
Normal file
BIN
corpus/1dace1035ef72ca4dd3f0c9c217634e2ac837c48
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