Compare commits
22 Commits
e59fee39c7
...
v0.0.8
Author | SHA1 | Date | |
---|---|---|---|
60cb274a15 | |||
687bc9c935 | |||
d50bb8bc80 | |||
f19b403f19 | |||
34cd210907 | |||
1a5da9e899 | |||
8ba9b04d8c | |||
d895be36d2 | |||
65f8462e88 | |||
46e01af027 | |||
c9d0d72684 | |||
9046dc5a8f | |||
e2927bf0fa | |||
75a2b8d06c | |||
76df63a9d7 | |||
9c5b38b09a | |||
7142dab7ae | |||
3db3d975fc | |||
982b31af34 | |||
cc716ef16b | |||
88bcc7b75c | |||
3e6be6bd83 |
@@ -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.7
|
VERSION 0.0.8
|
||||||
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"
|
||||||
|
259
ConflictSet.cpp
259
ConflictSet.cpp
@@ -606,6 +606,14 @@ template <class T> struct BoundedFreeListAllocator {
|
|||||||
VALGRIND_MAKE_MEM_NOACCESS(freeList, sizeof(T) + p->partialKeyCapacity);
|
VALGRIND_MAKE_MEM_NOACCESS(freeList, sizeof(T) + p->partialKeyCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoundedFreeListAllocator() = default;
|
||||||
|
|
||||||
|
BoundedFreeListAllocator(const BoundedFreeListAllocator &) = delete;
|
||||||
|
BoundedFreeListAllocator &
|
||||||
|
operator=(const BoundedFreeListAllocator &) = delete;
|
||||||
|
BoundedFreeListAllocator(BoundedFreeListAllocator &&) = delete;
|
||||||
|
BoundedFreeListAllocator &operator=(BoundedFreeListAllocator &&) = delete;
|
||||||
|
|
||||||
~BoundedFreeListAllocator() {
|
~BoundedFreeListAllocator() {
|
||||||
for (void *iter = freeList; iter != nullptr;) {
|
for (void *iter = freeList; iter != nullptr;) {
|
||||||
VALGRIND_MAKE_MEM_DEFINED(iter, sizeof(Node));
|
VALGRIND_MAKE_MEM_DEFINED(iter, sizeof(Node));
|
||||||
@@ -1875,7 +1883,8 @@ bool scan16(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
|
|||||||
|
|
||||||
uint16x4_t conflicting[4];
|
uint16x4_t conflicting[4];
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
conflicting[i] = vmovn_u32(vcgtq_s32(vsubq_u32(w4[i], rvVec), z));
|
conflicting[i] =
|
||||||
|
vmovn_u32(vcgtq_s32(vreinterpretq_s32_u32(vsubq_u32(w4[i], rvVec)), z));
|
||||||
}
|
}
|
||||||
auto combined =
|
auto combined =
|
||||||
vcombine_u8(vmovn_u16(vcombine_u16(conflicting[0], conflicting[1])),
|
vcombine_u8(vmovn_u16(vcombine_u16(conflicting[0], conflicting[1])),
|
||||||
@@ -1944,7 +1953,8 @@ scan16(const InternalVersionT *vs, int begin, int end,
|
|||||||
|
|
||||||
uint16x4_t conflicting[4];
|
uint16x4_t conflicting[4];
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
conflicting[i] = vmovn_u32(vcgtq_s32(vsubq_u32(w4[i], rvVec), z));
|
conflicting[i] =
|
||||||
|
vmovn_u32(vcgtq_s32(vreinterpretq_s32_u32(vsubq_u32(w4[i], rvVec)), z));
|
||||||
}
|
}
|
||||||
auto combined =
|
auto combined =
|
||||||
vcombine_u8(vmovn_u16(vcombine_u16(conflicting[0], conflicting[1])),
|
vcombine_u8(vmovn_u16(vcombine_u16(conflicting[0], conflicting[1])),
|
||||||
@@ -1991,55 +2001,163 @@ bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
|||||||
|
|
||||||
assert(!(begin == -1 && end == 256));
|
assert(!(begin == -1 && end == 256));
|
||||||
|
|
||||||
{
|
|
||||||
int c = getChildGeq(n, begin + 1);
|
|
||||||
if (c >= 0 && c < end) {
|
|
||||||
auto *child = getChildExists(n, c);
|
|
||||||
if (child->entryPresent) {
|
|
||||||
if (!(child->entry.rangeVersion <= readVersion)) {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
begin = c;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [begin, end) is now the half-open interval of children we're interested in.
|
|
||||||
assert(begin < end);
|
|
||||||
|
|
||||||
switch (n->getType()) {
|
switch (n->getType()) {
|
||||||
case Type_Node0: // GCOVR_EXCL_LINE
|
case Type_Node0:
|
||||||
// We would have returned above, after not finding a child
|
return true;
|
||||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
|
||||||
case Type_Node3: {
|
case Type_Node3: {
|
||||||
auto *self = static_cast<Node3 *>(n);
|
auto *self = static_cast<Node3 *>(n);
|
||||||
|
|
||||||
|
++begin;
|
||||||
|
|
||||||
const unsigned shiftUpperBound = end - begin;
|
const unsigned shiftUpperBound = end - begin;
|
||||||
const unsigned shiftAmount = begin;
|
const unsigned shiftAmount = begin;
|
||||||
auto inBounds = [&](unsigned c) {
|
auto inBounds = [&](unsigned c) {
|
||||||
return c - shiftAmount < shiftUpperBound;
|
return c - shiftAmount < shiftUpperBound;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t compared = 0;
|
|
||||||
for (int i = 0; i < Node3::kMaxNodes; ++i) {
|
|
||||||
compared |= (self->childMaxVersion[i] > readVersion) << i;
|
|
||||||
}
|
|
||||||
uint32_t mask = 0;
|
uint32_t mask = 0;
|
||||||
for (int i = 0; i < Node3::kMaxNodes; ++i) {
|
for (int i = 0; i < Node3::kMaxNodes; ++i) {
|
||||||
mask |= inBounds(self->index[i]) << i;
|
mask |= inBounds(self->index[i]) << i;
|
||||||
}
|
}
|
||||||
return !(compared & mask);
|
mask &= (1 << self->numChildren) - 1;
|
||||||
|
if (!mask) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto *child = self->children[__builtin_ctz(mask)];
|
||||||
|
const bool firstRangeOk =
|
||||||
|
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||||
|
uint32_t compared = 0;
|
||||||
|
for (int i = 0; i < Node3::kMaxNodes; ++i) {
|
||||||
|
compared |= (self->childMaxVersion[i] > readVersion) << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !(compared & mask) && firstRangeOk;
|
||||||
}
|
}
|
||||||
case Type_Node16: {
|
case Type_Node16: {
|
||||||
auto *self = static_cast<Node16 *>(n);
|
auto *self = static_cast<Node16 *>(n);
|
||||||
|
|
||||||
return scan16<kAVX512>(self->childMaxVersion, self->index, begin, end,
|
++begin;
|
||||||
readVersion);
|
|
||||||
|
assert(begin <= end);
|
||||||
|
assert(end - begin < 256);
|
||||||
|
|
||||||
|
#ifdef HAS_ARM_NEON
|
||||||
|
|
||||||
|
uint8x16_t indices;
|
||||||
|
memcpy(&indices, self->index, 16);
|
||||||
|
// 0xff for each in bounds
|
||||||
|
auto results =
|
||||||
|
vcltq_u8(vsubq_u8(indices, vdupq_n_u8(begin)), vdupq_n_u8(end - begin));
|
||||||
|
// 0xf for each 0xff
|
||||||
|
uint64_t mask = vget_lane_u64(
|
||||||
|
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(results), 4)), 0);
|
||||||
|
|
||||||
|
mask &= self->numChildren == 16
|
||||||
|
? uint64_t(-1)
|
||||||
|
: (uint64_t(1) << (self->numChildren << 2)) - 1;
|
||||||
|
if (!mask) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto *child = self->children[__builtin_ctzll(mask) >> 2];
|
||||||
|
const bool firstRangeOk =
|
||||||
|
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||||
|
|
||||||
|
uint32x4_t w4[4];
|
||||||
|
memcpy(w4, self->childMaxVersion, sizeof(w4));
|
||||||
|
uint32_t rv;
|
||||||
|
memcpy(&rv, &readVersion, sizeof(rv));
|
||||||
|
const auto rvVec = vdupq_n_u32(rv);
|
||||||
|
|
||||||
|
int32x4_t z;
|
||||||
|
memset(&z, 0, sizeof(z));
|
||||||
|
|
||||||
|
uint16x4_t conflicting[4];
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
conflicting[i] = vmovn_u32(
|
||||||
|
vcgtq_s32(vreinterpretq_s32_u32(vsubq_u32(w4[i], rvVec)), z));
|
||||||
|
}
|
||||||
|
auto combined =
|
||||||
|
vcombine_u8(vmovn_u16(vcombine_u16(conflicting[0], conflicting[1])),
|
||||||
|
vmovn_u16(vcombine_u16(conflicting[2], conflicting[3])));
|
||||||
|
|
||||||
|
uint64_t compared = vget_lane_u64(
|
||||||
|
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(combined), 4)), 0);
|
||||||
|
|
||||||
|
return !(compared & mask) && firstRangeOk;
|
||||||
|
|
||||||
|
#elif defined(HAS_AVX)
|
||||||
|
|
||||||
|
__m128i indices;
|
||||||
|
memcpy(&indices, self->index, 16);
|
||||||
|
indices = _mm_sub_epi8(indices, _mm_set1_epi8(begin));
|
||||||
|
uint32_t mask =
|
||||||
|
0xffff &
|
||||||
|
~_mm_movemask_epi8(_mm_cmpeq_epi8(
|
||||||
|
indices, _mm_max_epu8(indices, _mm_set1_epi8(end - begin))));
|
||||||
|
mask &= (1 << self->numChildren) - 1;
|
||||||
|
if (!mask) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto *child = self->children[__builtin_ctz(mask)];
|
||||||
|
const bool firstRangeOk =
|
||||||
|
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||||
|
|
||||||
|
uint32_t compared = 0;
|
||||||
|
if constexpr (kAVX512) {
|
||||||
|
compared = compare16_32bit_avx512(self->childMaxVersion, readVersion);
|
||||||
|
} else {
|
||||||
|
compared = compare16_32bit(self->childMaxVersion, readVersion);
|
||||||
|
}
|
||||||
|
return !(compared & mask) && firstRangeOk;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
const unsigned shiftUpperBound = end - begin;
|
||||||
|
const unsigned shiftAmount = begin;
|
||||||
|
auto inBounds = [&](unsigned c) {
|
||||||
|
return c - shiftAmount < shiftUpperBound;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t mask = 0;
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
mask |= inBounds(self->index[i]) << i;
|
||||||
|
}
|
||||||
|
mask &= (1 << self->numChildren) - 1;
|
||||||
|
if (!mask) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto *child = self->children[__builtin_ctz(mask)];
|
||||||
|
const bool firstRangeOk =
|
||||||
|
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||||
|
uint32_t compared = 0;
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
compared |= (self->childMaxVersion[i] > readVersion) << i;
|
||||||
|
}
|
||||||
|
return !(compared & mask) && firstRangeOk;
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
case Type_Node48: {
|
case Type_Node48: {
|
||||||
auto *self = static_cast<Node48 *>(n);
|
auto *self = static_cast<Node48 *>(n);
|
||||||
|
|
||||||
|
{
|
||||||
|
int c = self->bitSet.firstSetGeq(begin + 1);
|
||||||
|
if (c >= 0 && c < end) {
|
||||||
|
auto *child = self->children[self->index[c]];
|
||||||
|
if (child->entryPresent) {
|
||||||
|
if (!(child->entry.rangeVersion <= readVersion)) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
begin = c;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// [begin, end) is now the half-open interval of children we're interested
|
||||||
|
// in.
|
||||||
|
assert(begin < end);
|
||||||
|
}
|
||||||
|
|
||||||
// Check all pages
|
// Check all pages
|
||||||
static_assert(Node48::kMaxOfMaxPageSize == 16);
|
static_assert(Node48::kMaxOfMaxPageSize == 16);
|
||||||
for (int i = 0; i < Node48::kMaxOfMaxTotalPages; ++i) {
|
for (int i = 0; i < Node48::kMaxOfMaxTotalPages; ++i) {
|
||||||
@@ -2057,6 +2175,25 @@ bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
|||||||
case Type_Node256: {
|
case Type_Node256: {
|
||||||
static_assert(Node256::kMaxOfMaxTotalPages == 16);
|
static_assert(Node256::kMaxOfMaxTotalPages == 16);
|
||||||
auto *self = static_cast<Node256 *>(n);
|
auto *self = static_cast<Node256 *>(n);
|
||||||
|
|
||||||
|
{
|
||||||
|
int c = self->bitSet.firstSetGeq(begin + 1);
|
||||||
|
if (c >= 0 && c < end) {
|
||||||
|
auto *child = self->children[c];
|
||||||
|
if (child->entryPresent) {
|
||||||
|
if (!(child->entry.rangeVersion <= readVersion)) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
begin = c;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// [begin, end) is now the half-open interval of children we're interested
|
||||||
|
// in.
|
||||||
|
assert(begin < end);
|
||||||
|
}
|
||||||
|
|
||||||
const int firstPage = begin >> Node256::kMaxOfMaxShift;
|
const int firstPage = begin >> Node256::kMaxOfMaxShift;
|
||||||
const int lastPage = (end - 1) >> Node256::kMaxOfMaxShift;
|
const int lastPage = (end - 1) >> Node256::kMaxOfMaxShift;
|
||||||
// Check the only page if there's only one
|
// Check the only page if there's only one
|
||||||
@@ -2874,11 +3011,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
const auto &r = reads[i];
|
const auto &r = reads[i];
|
||||||
auto begin = std::span<const uint8_t>(r.begin.p, r.begin.len);
|
auto begin = std::span<const uint8_t>(r.begin.p, r.begin.len);
|
||||||
auto end = std::span<const uint8_t>(r.end.p, r.end.len);
|
auto end = std::span<const uint8_t>(r.end.p, r.end.len);
|
||||||
|
assert(oldestVersionFullPrecision >=
|
||||||
|
newestVersionFullPrecision - kNominalVersionWindow);
|
||||||
result[i] =
|
result[i] =
|
||||||
reads[i].readVersion < oldestVersionFullPrecision ||
|
reads[i].readVersion < oldestVersionFullPrecision ? TooOld
|
||||||
reads[i].readVersion <
|
|
||||||
newestVersionFullPrecision - kNominalVersionWindow
|
|
||||||
? TooOld
|
|
||||||
: (end.size() > 0
|
: (end.size() > 0
|
||||||
? checkRangeRead(root, begin, end,
|
? checkRangeRead(root, begin, end,
|
||||||
InternalVersionT(reads[i].readVersion), this)
|
InternalVersionT(reads[i].readVersion), this)
|
||||||
@@ -2892,8 +3028,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
void addWrites(const WriteRange *writes, int count, int64_t writeVersion) {
|
void addWrites(const WriteRange *writes, int count, int64_t writeVersion) {
|
||||||
assert(writeVersion >= newestVersionFullPrecision);
|
assert(writeVersion >= newestVersionFullPrecision);
|
||||||
|
|
||||||
// TODO allow this condition
|
if (writeVersion > newestVersionFullPrecision + kNominalVersionWindow) {
|
||||||
assert(writeVersion < newestVersionFullPrecision + kNominalVersionWindow);
|
destroyTree(root);
|
||||||
|
init(writeVersion - kNominalVersionWindow);
|
||||||
|
}
|
||||||
|
|
||||||
newestVersionFullPrecision = writeVersion;
|
newestVersionFullPrecision = writeVersion;
|
||||||
setOldestVersion(
|
setOldestVersion(
|
||||||
@@ -2985,14 +3123,19 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
keyUpdates = gcScanStep(keyUpdates);
|
keyUpdates = gcScanStep(keyUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit Impl(int64_t oldestVersion)
|
int64_t getBytes() const { return totalBytes; }
|
||||||
: oldestVersion(oldestVersion), oldestVersionFullPrecision(oldestVersion),
|
|
||||||
oldestExtantVersion(oldestVersion),
|
void init(int64_t oldestVersion) {
|
||||||
oldestVersionAtGcBegin(oldestVersion),
|
this->oldestVersion = InternalVersionT(oldestVersion);
|
||||||
newestVersionFullPrecision(oldestVersion) {
|
oldestVersionFullPrecision = oldestExtantVersion = oldestVersionAtGcBegin =
|
||||||
#if DEBUG_VERBOSE
|
newestVersionFullPrecision = oldestVersion;
|
||||||
fprintf(stderr, "radix_tree: create\n");
|
|
||||||
#endif
|
allocators.~NodeAllocators();
|
||||||
|
new (&allocators) NodeAllocators();
|
||||||
|
|
||||||
|
removalKeyArena = Arena{};
|
||||||
|
removalKey = {};
|
||||||
|
keyUpdates = 10;
|
||||||
|
|
||||||
// Insert ""
|
// Insert ""
|
||||||
root = allocators.node0.allocate(0);
|
root = allocators.node0.allocate(0);
|
||||||
@@ -3009,26 +3152,22 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
root->entry.rangeVersion = this->oldestVersion;
|
root->entry.rangeVersion = this->oldestVersion;
|
||||||
|
|
||||||
InternalVersionT::zero = this->oldestVersion;
|
InternalVersionT::zero = this->oldestVersion;
|
||||||
|
|
||||||
|
// Intentionally not resetting totalBytes
|
||||||
}
|
}
|
||||||
~Impl() {
|
|
||||||
#if DEBUG_VERBOSE
|
explicit Impl(int64_t oldestVersion) { init(oldestVersion); }
|
||||||
fprintf(stderr, "radix_tree: destroy\n");
|
~Impl() { destroyTree(root); }
|
||||||
#endif
|
|
||||||
destroyTree(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeAllocators allocators;
|
NodeAllocators allocators;
|
||||||
|
|
||||||
Arena removalKeyArena;
|
Arena removalKeyArena;
|
||||||
std::span<const uint8_t> removalKey;
|
std::span<const uint8_t> removalKey;
|
||||||
int64_t keyUpdates = 10;
|
int64_t keyUpdates;
|
||||||
|
|
||||||
Node *root;
|
Node *root;
|
||||||
InternalVersionT rootMaxVersion;
|
InternalVersionT rootMaxVersion;
|
||||||
InternalVersionT oldestVersion;
|
InternalVersionT oldestVersion;
|
||||||
// TODO this doesn't fully mitigate the 32-bit precision issue, since we still
|
|
||||||
// need to make sure we clean up versions in the tree before they fall out of
|
|
||||||
// the `kMaxCorrectVersionWindow` window.
|
|
||||||
int64_t oldestVersionFullPrecision;
|
int64_t oldestVersionFullPrecision;
|
||||||
int64_t oldestExtantVersion;
|
int64_t oldestExtantVersion;
|
||||||
int64_t oldestVersionAtGcBegin;
|
int64_t oldestVersionAtGcBegin;
|
||||||
@@ -3163,7 +3302,7 @@ void internal_destroy(ConflictSet::Impl *impl) {
|
|||||||
safe_free(impl, sizeof(ConflictSet::Impl));
|
safe_free(impl, sizeof(ConflictSet::Impl));
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->totalBytes; }
|
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->getBytes(); }
|
||||||
|
|
||||||
// ==================== END IMPLEMENTATION ====================
|
// ==================== END IMPLEMENTATION ====================
|
||||||
|
|
||||||
@@ -3191,11 +3330,7 @@ Iterator firstGeqLogical(Node *n, const std::span<const uint8_t> key) {
|
|||||||
} else {
|
} else {
|
||||||
n = nextSibling(n);
|
n = nextSibling(n);
|
||||||
if (n == nullptr) {
|
if (n == nullptr) {
|
||||||
// This line is genuinely unreachable from any entry point of the
|
return {nullptr, 1};
|
||||||
// final library, since we can't remove a key without introducing a
|
|
||||||
// key after it, and the only production caller of firstGeq is for
|
|
||||||
// resuming the setOldestVersion scan.
|
|
||||||
return {nullptr, 1}; // GCOVR_EXCL_LINE
|
|
||||||
}
|
}
|
||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
}
|
}
|
||||||
@@ -3476,8 +3611,8 @@ void checkVersionsGeqOldestExtant(Node *n,
|
|||||||
assert(m >= oldestExtantVersion);
|
assert(m >= oldestExtantVersion);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
default: // GCOVR_EXCL_LINE
|
default:
|
||||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
59
Internal.h
59
Internal.h
@@ -518,7 +518,7 @@ struct ReferenceImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setOldestVersion(int64_t oldestVersion) {
|
void setOldestVersion(int64_t oldestVersion) {
|
||||||
assert(oldestVersion >= oldestVersion);
|
assert(oldestVersion >= this->oldestVersion);
|
||||||
this->oldestVersion = oldestVersion;
|
this->oldestVersion = oldestVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,8 +583,8 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
explicit TestDriver(const uint8_t *data, size_t size)
|
explicit TestDriver(const uint8_t *data, size_t size)
|
||||||
: arbitrary({data, size}) {}
|
: arbitrary({data, size}) {}
|
||||||
|
|
||||||
int64_t oldestVersion = 0;
|
int64_t oldestVersion = arbitrary.next();
|
||||||
int64_t writeVersion = 0;
|
int64_t writeVersion = oldestVersion;
|
||||||
ConflictSetImpl cs{oldestVersion};
|
ConflictSetImpl cs{oldestVersion};
|
||||||
ReferenceImpl refImpl{oldestVersion};
|
ReferenceImpl refImpl{oldestVersion};
|
||||||
|
|
||||||
@@ -598,6 +598,7 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
// 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);
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary.hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -605,7 +606,8 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
{
|
{
|
||||||
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(2e9));
|
int64_t v = (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);
|
||||||
@@ -662,21 +664,64 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
fprintf(stderr, "Write @ %" PRId64 "\n", v);
|
fprintf(stderr, "Write @ %" PRId64 "\n", v);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Test non-canonical writes
|
||||||
|
if (numPointWrites > 0) {
|
||||||
|
int overlaps = arbitrary.bounded(numPointWrites);
|
||||||
|
for (int i = 0; i < numPointWrites + numRangeWrites && overlaps > 0;
|
||||||
|
++i) {
|
||||||
|
if (writes[i].end.len == 0) {
|
||||||
|
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
||||||
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
|
memset(begin, prefixByte, prefixLen);
|
||||||
|
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
|
writes[i].end.len = keyLen;
|
||||||
|
writes[i].end.p = begin;
|
||||||
|
auto c =
|
||||||
|
std::span<const uint8_t>(writes[i].begin.p,
|
||||||
|
writes[i].begin.len) <=>
|
||||||
|
std::span<const uint8_t>(writes[i].end.p, writes[i].end.len);
|
||||||
|
if (c > 0) {
|
||||||
|
using std::swap;
|
||||||
|
swap(writes[i].begin, writes[i].end);
|
||||||
|
} else if (c == 0) {
|
||||||
|
// It's a point write after all, I guess
|
||||||
|
writes[i].end.len = 0;
|
||||||
|
}
|
||||||
|
--overlaps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arbitrary.bounded(2)) {
|
||||||
|
// Shuffle writes
|
||||||
|
for (int i = numPointWrites + numRangeWrites - 1; i > 0; --i) {
|
||||||
|
int j = arbitrary.bounded(i + 1);
|
||||||
|
if (i != j) {
|
||||||
|
using std::swap;
|
||||||
|
swap(writes[i], writes[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CALLGRIND_START_INSTRUMENTATION;
|
CALLGRIND_START_INSTRUMENTATION;
|
||||||
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
CALLGRIND_STOP_INSTRUMENTATION;
|
CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
|
|
||||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
|
|
||||||
oldestVersion =
|
oldestVersion +=
|
||||||
std::min(writeVersion - 10, oldestVersion + arbitrary.bounded(10));
|
arbitrary.bounded(10) ? arbitrary.bounded(10) : arbitrary.next();
|
||||||
|
oldestVersion = std::min(oldestVersion, writeVersion);
|
||||||
cs.setOldestVersion(oldestVersion);
|
cs.setOldestVersion(oldestVersion);
|
||||||
refImpl.setOldestVersion(oldestVersion);
|
refImpl.setOldestVersion(oldestVersion);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
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), 0);
|
|
||||||
|
int64_t v = std::max<int64_t>(writeVersion - (arbitrary.bounded(10)
|
||||||
|
? arbitrary.bounded(10)
|
||||||
|
: arbitrary.next()),
|
||||||
|
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);
|
||||||
|
11
Jenkinsfile
vendored
11
Jenkinsfile
vendored
@@ -59,17 +59,6 @@ pipeline {
|
|||||||
CleanBuildAndTest("-DUSE_SIMD_FALLBACK=ON")
|
CleanBuildAndTest("-DUSE_SIMD_FALLBACK=ON")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('32-bit versions') {
|
|
||||||
agent {
|
|
||||||
dockerfile {
|
|
||||||
args '-v /home/jenkins/ccache:/ccache'
|
|
||||||
reuseNode true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
CleanBuildAndTest("-DUSE_32_BIT_VERSIONS=ON")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Release [gcc]') {
|
stage('Release [gcc]') {
|
||||||
agent {
|
agent {
|
||||||
dockerfile {
|
dockerfile {
|
||||||
|
32
README.md
32
README.md
@@ -60,27 +60,27 @@ Performance counters:
|
|||||||
|
|
||||||
| ns/op | op/s | err% | total | benchmark
|
| ns/op | op/s | err% | total | benchmark
|
||||||
|--------------------:|--------------------:|--------:|----------:|:----------
|
|--------------------:|--------------------:|--------:|----------:|:----------
|
||||||
| 256.89 | 3,892,784.92 | 0.3% | 0.01 | `point reads`
|
| 245.99 | 4,065,232.81 | 0.3% | 0.01 | `point reads`
|
||||||
| 272.90 | 3,664,395.04 | 0.2% | 0.01 | `prefix reads`
|
| 265.93 | 3,760,430.49 | 0.2% | 0.01 | `prefix reads`
|
||||||
| 507.22 | 1,971,549.50 | 0.7% | 0.01 | `range reads`
|
| 485.30 | 2,060,569.50 | 0.2% | 0.01 | `range reads`
|
||||||
| 452.66 | 2,209,181.91 | 0.5% | 0.01 | `point writes`
|
| 449.60 | 2,224,195.17 | 0.4% | 0.01 | `point writes`
|
||||||
| 438.09 | 2,282,619.96 | 0.4% | 0.01 | `prefix writes`
|
| 441.76 | 2,263,688.18 | 1.1% | 0.01 | `prefix writes`
|
||||||
| 253.33 | 3,947,420.36 | 2.5% | 0.02 | `range writes`
|
| 245.42 | 4,074,647.54 | 2.4% | 0.02 | `range writes`
|
||||||
| 574.07 | 1,741,936.71 | 0.3% | 0.01 | `monotonic increasing point writes`
|
| 572.80 | 1,745,810.06 | 1.3% | 0.01 | `monotonic increasing point writes`
|
||||||
| 151,562.50 | 6,597.94 | 1.5% | 0.01 | `worst case for radix tree`
|
| 154,819.33 | 6,459.14 | 0.9% | 0.01 | `worst case for radix tree`
|
||||||
|
|
||||||
## Radix tree (this implementation)
|
## Radix tree (this implementation)
|
||||||
|
|
||||||
| ns/op | op/s | err% | total | benchmark
|
| ns/op | op/s | err% | total | benchmark
|
||||||
|--------------------:|--------------------:|--------:|----------:|:----------
|
|--------------------:|--------------------:|--------:|----------:|:----------
|
||||||
| 19.83 | 50,420,955.28 | 0.1% | 0.01 | `point reads`
|
| 19.17 | 52,163,930.66 | 0.1% | 0.01 | `point reads`
|
||||||
| 55.95 | 17,872,542.40 | 0.5% | 0.01 | `prefix reads`
|
| 23.68 | 42,224,388.21 | 0.7% | 0.01 | `prefix reads`
|
||||||
| 88.28 | 11,327,709.50 | 0.4% | 0.01 | `range reads`
|
| 63.30 | 15,797,506.06 | 0.9% | 0.01 | `range reads`
|
||||||
| 29.15 | 34,309,531.64 | 0.5% | 0.01 | `point writes`
|
| 29.66 | 33,720,994.74 | 0.3% | 0.01 | `point writes`
|
||||||
| 42.36 | 23,607,424.27 | 1.1% | 0.01 | `prefix writes`
|
| 43.50 | 22,987,781.25 | 1.0% | 0.01 | `prefix writes`
|
||||||
| 50.00 | 20,000,000.00 | 0.0% | 0.01 | `range writes`
|
| 50.00 | 20,000,000.00 | 0.8% | 0.01 | `range writes`
|
||||||
| 93.52 | 10,692,413.79 | 3.3% | 0.01 | `monotonic increasing point writes`
|
| 103.25 | 9,684,786.47 | 2.9% | 0.01 | `monotonic increasing point writes`
|
||||||
| 2,388,417.00 | 418.69 | 0.4% | 0.03 | `worst case for radix tree`
|
| 1,181,500.00 | 846.38 | 2.3% | 0.01 | `worst case for radix tree`
|
||||||
|
|
||||||
# "Real data" test
|
# "Real data" test
|
||||||
|
|
||||||
|
173
SkipList.cpp
173
SkipList.cpp
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#include "ConflictSet.h"
|
#include "ConflictSet.h"
|
||||||
#include "Internal.h"
|
#include "Internal.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
std::span<const uint8_t> keyAfter(Arena &arena, std::span<const uint8_t> key) {
|
std::span<const uint8_t> keyAfter(Arena &arena, std::span<const uint8_t> key) {
|
||||||
@@ -52,6 +54,135 @@ struct KeyRangeRef {
|
|||||||
: begin(begin), end(keyAfter(arena, begin)) {}
|
: begin(begin), end(keyAfter(arena, begin)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct KeyInfo {
|
||||||
|
StringRef key;
|
||||||
|
bool begin;
|
||||||
|
bool write;
|
||||||
|
|
||||||
|
KeyInfo() = default;
|
||||||
|
KeyInfo(StringRef key, bool begin, bool write)
|
||||||
|
: key(key), begin(begin), write(write) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
force_inline int extra_ordering(const KeyInfo &ki) {
|
||||||
|
return ki.begin * 2 + (ki.write ^ ki.begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if done with string
|
||||||
|
force_inline bool getCharacter(const KeyInfo &ki, int character,
|
||||||
|
int &outputCharacter) {
|
||||||
|
// normal case
|
||||||
|
if (character < ki.key.size()) {
|
||||||
|
outputCharacter = 5 + ki.key.begin()[character];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// termination
|
||||||
|
if (character == ki.key.size()) {
|
||||||
|
outputCharacter = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character == ki.key.size() + 1) {
|
||||||
|
// end/begin+read/write relative sorting
|
||||||
|
outputCharacter = extra_ordering(ki);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputCharacter = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const KeyInfo &lhs, const KeyInfo &rhs) {
|
||||||
|
int i = std::min(lhs.key.size(), rhs.key.size());
|
||||||
|
int c = memcmp(lhs.key.data(), rhs.key.data(), i);
|
||||||
|
if (c != 0)
|
||||||
|
return c < 0;
|
||||||
|
|
||||||
|
// Always sort shorter keys before longer keys.
|
||||||
|
if (lhs.key.size() < rhs.key.size()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (lhs.key.size() > rhs.key.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the keys are the same length, use the extra ordering constraint.
|
||||||
|
return extra_ordering(lhs) < extra_ordering(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const KeyInfo &lhs, const KeyInfo &rhs) {
|
||||||
|
return !(lhs < rhs || rhs < lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swapSort(std::vector<KeyInfo> &points, int a, int b) {
|
||||||
|
if (points[b] < points[a]) {
|
||||||
|
KeyInfo temp;
|
||||||
|
temp = points[a];
|
||||||
|
points[a] = points[b];
|
||||||
|
points[b] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SortTask {
|
||||||
|
int begin;
|
||||||
|
int size;
|
||||||
|
int character;
|
||||||
|
SortTask(int begin, int size, int character)
|
||||||
|
: begin(begin), size(size), character(character) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void sortPoints(std::vector<KeyInfo> &points) {
|
||||||
|
std::vector<SortTask> tasks;
|
||||||
|
std::vector<KeyInfo> newPoints;
|
||||||
|
std::vector<int> counts;
|
||||||
|
|
||||||
|
tasks.emplace_back(0, points.size(), 0);
|
||||||
|
|
||||||
|
while (tasks.size()) {
|
||||||
|
SortTask st = tasks.back();
|
||||||
|
tasks.pop_back();
|
||||||
|
|
||||||
|
if (st.size < 10) {
|
||||||
|
std::sort(points.begin() + st.begin, points.begin() + st.begin + st.size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newPoints.resize(st.size);
|
||||||
|
counts.assign(256 + 5, 0);
|
||||||
|
|
||||||
|
// get counts
|
||||||
|
int c;
|
||||||
|
bool allDone = true;
|
||||||
|
for (int i = st.begin; i < st.begin + st.size; i++) {
|
||||||
|
allDone &= getCharacter(points[i], st.character, c);
|
||||||
|
counts[c]++;
|
||||||
|
}
|
||||||
|
if (allDone)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// calculate offsets from counts and build next level of tasks
|
||||||
|
int total = 0;
|
||||||
|
for (int i = 0; i < counts.size(); i++) {
|
||||||
|
int temp = counts[i];
|
||||||
|
if (temp > 1)
|
||||||
|
tasks.emplace_back(st.begin + total, temp, st.character + 1);
|
||||||
|
counts[i] = total;
|
||||||
|
total += temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put in their places
|
||||||
|
for (int i = st.begin; i < st.begin + st.size; i++) {
|
||||||
|
getCharacter(points[i], st.character, c);
|
||||||
|
newPoints[counts[c]++] = points[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy back into original points array
|
||||||
|
for (int i = 0; i < st.size; i++)
|
||||||
|
points[st.begin + i] = newPoints[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static thread_local uint32_t g_seed = 0;
|
static thread_local uint32_t g_seed = 0;
|
||||||
|
|
||||||
static inline int skfastrand() {
|
static inline int skfastrand() {
|
||||||
@@ -602,10 +733,40 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
|
|
||||||
void addWrites(const ConflictSet::WriteRange *writes, int count,
|
void addWrites(const ConflictSet::WriteRange *writes, int count,
|
||||||
int64_t writeVersion) {
|
int64_t writeVersion) {
|
||||||
|
auto points = std::vector<KeyInfo>(count * 2);
|
||||||
|
Arena arena;
|
||||||
|
|
||||||
|
for (int r = 0; r < count; r++) {
|
||||||
|
points.emplace_back(StringRef(writes[r].begin.p, writes[r].begin.len),
|
||||||
|
true, true);
|
||||||
|
points.emplace_back(
|
||||||
|
writes[r].end.len > 0
|
||||||
|
? StringRef{writes[r].end.p, size_t(writes[r].end.len)}
|
||||||
|
: keyAfter(arena, points.back().key),
|
||||||
|
false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
sortPoints(points);
|
||||||
|
|
||||||
|
int activeWriteCount = 0;
|
||||||
|
std::vector<std::pair<StringRef, StringRef>> combinedWriteConflictRanges;
|
||||||
|
for (const KeyInfo &point : points) {
|
||||||
|
if (point.write) {
|
||||||
|
if (point.begin) {
|
||||||
|
activeWriteCount++;
|
||||||
|
if (activeWriteCount == 1)
|
||||||
|
combinedWriteConflictRanges.emplace_back(point.key, StringRef());
|
||||||
|
} else /*if (point.end)*/ {
|
||||||
|
activeWriteCount--;
|
||||||
|
if (activeWriteCount == 0)
|
||||||
|
combinedWriteConflictRanges.back().second = point.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assert(writeVersion >= newestVersion);
|
assert(writeVersion >= newestVersion);
|
||||||
newestVersion = writeVersion;
|
newestVersion = writeVersion;
|
||||||
Arena arena;
|
const int stringCount = combinedWriteConflictRanges.size() * 2;
|
||||||
const int stringCount = count * 2;
|
|
||||||
|
|
||||||
const int stripeSize = 16;
|
const int stripeSize = 16;
|
||||||
SkipList::Finger fingers[stripeSize];
|
SkipList::Finger fingers[stripeSize];
|
||||||
@@ -616,15 +777,13 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
int ss = stringCount - (stripes - 1) * stripeSize;
|
int ss = stringCount - (stripes - 1) * stripeSize;
|
||||||
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 = writes[s * stripeSize / 2 + i];
|
const auto &w = combinedWriteConflictRanges[s * stripeSize / 2 + i];
|
||||||
#if DEBUG_VERBOSE
|
#if DEBUG_VERBOSE
|
||||||
printf("Write begin: %s\n", printable(w.begin).c_str());
|
printf("Write begin: %s\n", printable(w.begin).c_str());
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
values[i * 2] = {w.begin.p, size_t(w.begin.len)};
|
values[i * 2] = w.first;
|
||||||
values[i * 2 + 1] = w.end.len > 0
|
values[i * 2 + 1] = w.second;
|
||||||
? StringRef{w.end.p, size_t(w.end.len)}
|
|
||||||
: keyAfter(arena, values[i * 2]);
|
|
||||||
keyUpdates += 3;
|
keyUpdates += 3;
|
||||||
}
|
}
|
||||||
skipList.find(values, fingers, temp, ss);
|
skipList.find(values, fingers, temp, ss);
|
||||||
|
Binary file not shown.
BIN
corpus/005e2b0059b0261bc2288a5843a31e098d31013b
Normal file
BIN
corpus/005e2b0059b0261bc2288a5843a31e098d31013b
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0164498b5d5fbc2a3a5979deec1a0446c0e1abb6
Normal file
BIN
corpus/0164498b5d5fbc2a3a5979deec1a0446c0e1abb6
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/01cab80c8efd804c267cc9242a12a4dac2959f98
Normal file
BIN
corpus/01cab80c8efd804c267cc9242a12a4dac2959f98
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/027cb1a49430f1677a7bb0510841e2078c69a40e
Normal file
BIN
corpus/027cb1a49430f1677a7bb0510841e2078c69a40e
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/037a852532d83bba4b2a366b1c2e88902ec43a62
Normal file
BIN
corpus/037a852532d83bba4b2a366b1c2e88902ec43a62
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/04cddf0d0e2f0466d26efa1595a76858bcde4c94
Normal file
BIN
corpus/04cddf0d0e2f0466d26efa1595a76858bcde4c94
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/076b27f409c8bda741fb719e5d10681e5ae1db31
Normal file
BIN
corpus/076b27f409c8bda741fb719e5d10681e5ae1db31
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/091f1883731c2f0ba9d3705b44bfcd6dd3bf88db
Normal file
BIN
corpus/091f1883731c2f0ba9d3705b44bfcd6dd3bf88db
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0ae06dc325d95c19967ac97b3de10c2fc8983b1b
Normal file
BIN
corpus/0ae06dc325d95c19967ac97b3de10c2fc8983b1b
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0b626de7d1730e4a677757381713ba32ddbc943c
Normal file
BIN
corpus/0b626de7d1730e4a677757381713ba32ddbc943c
Normal file
Binary file not shown.
BIN
corpus/0b6558613333c201962d579fad084b280bd96aa7
Normal file
BIN
corpus/0b6558613333c201962d579fad084b280bd96aa7
Normal file
Binary file not shown.
BIN
corpus/0b82dea314f067dc8fd7b52459c1b855c784fde4
Normal file
BIN
corpus/0b82dea314f067dc8fd7b52459c1b855c784fde4
Normal file
Binary file not shown.
BIN
corpus/0bc38a2aff322bfcf5ca402f996ba12f8daf31d1
Normal file
BIN
corpus/0bc38a2aff322bfcf5ca402f996ba12f8daf31d1
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0cabceb6692bc5ad27cffc1f00d412e6c1b0afc0
Normal file
BIN
corpus/0cabceb6692bc5ad27cffc1f00d412e6c1b0afc0
Normal file
Binary file not shown.
BIN
corpus/0cb1c7a2c5ad6f089cd3b2d48c658974aa338b2c
Normal file
BIN
corpus/0cb1c7a2c5ad6f089cd3b2d48c658974aa338b2c
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0da6dc7ca616a5de655f860192079e4859382371
Normal file
BIN
corpus/0da6dc7ca616a5de655f860192079e4859382371
Normal file
Binary file not shown.
BIN
corpus/0ef85a238153205a34565c63f4ad6c373a90b73e
Normal file
BIN
corpus/0ef85a238153205a34565c63f4ad6c373a90b73e
Normal file
Binary file not shown.
BIN
corpus/0f045e5e1a36ee449803f31d0ec334fb1218cc33
Normal file
BIN
corpus/0f045e5e1a36ee449803f31d0ec334fb1218cc33
Normal file
Binary file not shown.
BIN
corpus/0f2e401e0fe0e1d6267142355cc156ee7d2c3c87
Normal file
BIN
corpus/0f2e401e0fe0e1d6267142355cc156ee7d2c3c87
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0fc692696f94afbb5d2027bc12fa3dc4a19ac3a9
Normal file
BIN
corpus/0fc692696f94afbb5d2027bc12fa3dc4a19ac3a9
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/113ddcf047b2f8c3684853b0f086ead6f056fac2
Normal file
BIN
corpus/113ddcf047b2f8c3684853b0f086ead6f056fac2
Normal file
Binary file not shown.
BIN
corpus/11510f8502ab47f7d57cc205f3d6af50f36eb98c
Normal file
BIN
corpus/11510f8502ab47f7d57cc205f3d6af50f36eb98c
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/11919ca53b7efc88c8501dddde8fad916197f54c
Normal file
BIN
corpus/11919ca53b7efc88c8501dddde8fad916197f54c
Normal file
Binary file not shown.
BIN
corpus/12a815319620aa136dd77ceb9f6389fd74765f8d
Normal file
BIN
corpus/12a815319620aa136dd77ceb9f6389fd74765f8d
Normal file
Binary file not shown.
BIN
corpus/133c0de112bdcb419b188a1fe8574941517764e8
Normal file
BIN
corpus/133c0de112bdcb419b188a1fe8574941517764e8
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/135debe2b0cde6a9d63eb07116d7c85c8dbcc268
Normal file
BIN
corpus/135debe2b0cde6a9d63eb07116d7c85c8dbcc268
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/14ba3bc0137f0791498781c534a4a81c41e5f565
Normal file
BIN
corpus/14ba3bc0137f0791498781c534a4a81c41e5f565
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1534c7f0f2bac8015e9df650deb62fe383c58440
Normal file
BIN
corpus/1534c7f0f2bac8015e9df650deb62fe383c58440
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1681caabc3d5e8d79b4dd79a465a3678bc6ab498
Normal file
BIN
corpus/1681caabc3d5e8d79b4dd79a465a3678bc6ab498
Normal file
Binary file not shown.
BIN
corpus/16da9b15792f7c488d3d9f8354199cc512cd012f
Normal file
BIN
corpus/16da9b15792f7c488d3d9f8354199cc512cd012f
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/17f44a42bb6e083988b3eb00ee6f14dccc10bcbf
Normal file
BIN
corpus/17f44a42bb6e083988b3eb00ee6f14dccc10bcbf
Normal file
Binary file not shown.
BIN
corpus/180e912ab1eee5ccaa1cab79c9fd4d81d2009b6a
Normal file
BIN
corpus/180e912ab1eee5ccaa1cab79c9fd4d81d2009b6a
Normal file
Binary file not shown.
BIN
corpus/1906d18823ade0beb690c68d8585dfb3b565956f
Normal file
BIN
corpus/1906d18823ade0beb690c68d8585dfb3b565956f
Normal file
Binary file not shown.
BIN
corpus/199d8d206d74e0e77ac21334cfe398a3885d84a2
Normal file
BIN
corpus/199d8d206d74e0e77ac21334cfe398a3885d84a2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1ae168891529281e4d601dbbe83a064fb330e722
Normal file
BIN
corpus/1ae168891529281e4d601dbbe83a064fb330e722
Normal file
Binary file not shown.
BIN
corpus/1af2516afd85fafa8a6c40dae32059f8b60536b0
Normal file
BIN
corpus/1af2516afd85fafa8a6c40dae32059f8b60536b0
Normal file
Binary file not shown.
BIN
corpus/1af26a82f432e06da3d093435c1baa2251955ee7
Normal file
BIN
corpus/1af26a82f432e06da3d093435c1baa2251955ee7
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1c2b4d5da82dc79063550e5cf0c15b867098b47c
Normal file
BIN
corpus/1c2b4d5da82dc79063550e5cf0c15b867098b47c
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1cbd253ecbe54b30051695216915f273716fb7eb
Normal file
BIN
corpus/1cbd253ecbe54b30051695216915f273716fb7eb
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1d4c3da489e29554871120e2f9eb1227c92e9737
Normal file
BIN
corpus/1d4c3da489e29554871120e2f9eb1227c92e9737
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1f03804706499daec8988095a00faa51cffa139f
Normal file
BIN
corpus/1f03804706499daec8988095a00faa51cffa139f
Normal file
Binary file not shown.
BIN
corpus/1f3e8fba12eab3f4019d41a4818bf20599af0f20
Normal file
BIN
corpus/1f3e8fba12eab3f4019d41a4818bf20599af0f20
Normal file
Binary file not shown.
BIN
corpus/2002d20caa25f4a189811044fc8605ded3b8e7de
Normal file
BIN
corpus/2002d20caa25f4a189811044fc8605ded3b8e7de
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